<commit_before>window.cls || (window.cls = {});

cls.ScriptSelect = function(id, class_name)
{

  var _selected_value = "";
  var _selected_script_id = 0;
  var _stopped_script_id = "";

  this.getSelectedOptionTooltipText = function()
  {
    _selected_script_id = runtimes.getSelectedScript();
    var script = _selected_script_id && runtimes.getScript(_selected_script_id);
    return script && script.script_type == "linked" && script.uri || null;
  }

  this.getSelectedOptionText = function()
  {
    _selected_script_id = runtimes.getSelectedScript();
    if (_selected_script_id)
    {
      var script = runtimes.getScript(_selected_script_id);
      if (script)
      {
        var script_type = script.script_type.capitalize(true);
        return script.uri
             ? script.filename
             : script_type + " – " + (script.script_data.replace(/\s+/g, " ").slice(0, 300) ||
               ui_strings.S_TEXT_ECMA_SCRIPT_SCRIPT_ID + ': ' + script.script_id);
      }
      else if(_selected_script_id == -1)
      {
        return " ";
      }
      else
      {
        opera.postError(ui_strings.S_DRAGONFLY_INFO_MESSAGE +
          'missing script in getSelectedOptionText in cls.ScriptSelect');
      }
    }
    else if(runtimes.getSelectedRuntimeId() &&
            runtimes.isReloadedWindow(runtimes.getActiveWindowId()))
    {
      return ui_strings.S_INFO_RUNTIME_HAS_NO_SCRIPTS;
    }
    return '';
  }

  this.getSelectedOptionValue = function()
  {

  }

  this.templateOptionList = function(select_obj)
  {
    this._runtimes = runtimes.get_dom_runtimes(true);
    this._selected_script = runtimes.getSelectedScript();
    return templates.script_dropdown(this._id,
                                     this._runtimes,
                                     _stopped_script_id,
                                     this._selected_script);
  };

  this.onshowoptionlist = function(container)
  {
    var input = container.querySelector("input");
    if (input)
    {
      this._input = input;
      this._clear_button = container.querySelector(".js-dd-clear-filter");
      this._container = container;
      this._script_list = container.querySelector(".js-dd-script-list");
      this._filter.setContainer(container);
      this._filter.setFormInput(input);
      input.focus();
      this._get_option_ele_list();
      this._update_clear_button_state();
    }
  };

  this.onhideoptionlist = function()
  {
    this._filter.cleanup();
    this._filter.set_search_term("");
    this._input = null;
    this._clear_button = null;
    this._container = null;
    this._option_eles = null;
    this._option_box = null;
    this._option_ele_cursor = 0;
  }

  this.checkChange = function(target_ele)
  {
    var script_id_str = target_ele.get_attr('parent-node-chain', 'script-id');
    var script_id = script_id_str && parseInt(script_id_str);
    if (script_id)
    {
      // TODO is this needed?
      if (script_id != _selected_script_id)
      {
        runtimes.setSelectedScript(script_id);
        topCell.showView(views.js_source.id);
        _selected_script_id = script_id;
      }
      _selected_value = target_ele.textContent;
      return true;
    }
    return false;
  }

  // this.updateElement

  var onThreadStopped = function(msg)
  {
    _stopped_script_id = msg.stop_at.script_id;
  }

  var onThreadContinue = function(msg)
  {
    _stopped_script_id = "";
  }

  var onApplicationSetup = function()
  {
    eventHandlers.change['set-tab-size']({target: {value:  settings.js_source.get('tab-size')}});
  }

  this._onfilterinput = function(event, target)
  {
    this._update_clear_button_state();
    this._filter.searchDelayed(target.value);
  };

  this._update_clear_button_state = function()
  {
    if (this._input && this._clear_button)
    {
      if (this._input.value && !this._is_clear_button_visible)
      {
        this._is_clear_button_visible = true;
        this._clear_button.addClass("js-dd-visible");
      }
      else if (!this._input.value && this._is_clear_button_visible)
      {
        this._is_clear_button_visible = false;
        this._clear_button.removeClass("js-dd-visible");
      }
    }
  };

  this._onshortcut = function(action_id, event, target)
  {
    switch (action_id)
    {
      case "highlight-next-match":
      case "highlight-previous-match":
        var target = this._option_eles[this._option_ele_cursor];
        if (target)
        {
          if (!this._setting)
            this._init_match_history();

          if (this._input && !this._match_history.contains(this._input.value))
          {
            this._match_history.push(this._input.value);
            while (this._match_history.length > 10)
              this._match_history.shift();

            if (this._setting)
              this._setting.set("js-dd-match-history", this._match_history);
          }
          target.dispatchMouseEvent("mouseup");
        }
        return false;

      case "up":
        if (this._option_eles.length > 1)
        {
          var index = this._option_ele_cursor - 1;
          if (index < 0)
            index = this._option_eles.length - 1;

          this._move_highlight(index);
        }
        return false;

      case "down":
        if (this._option_eles.length > 1)
        {
          var index = this._option_ele_cursor + 1;
          if (index > this._option_eles.length - 1)
            index = 0;

          this._move_highlight(index);
        }
        return false;

      case "shift-up":
        if (!this._setting)
          this._init_match_history();

        this._match_cursor++;
        if (this._match_cursor > this._match_history.length - 1)
          this._match_cursor = 0;

        this._set_filter_value();
        return false;

      case "shift-down":
        if (!this._setting)
          this._init_match_history();

        this._match_cursor--;
        if (this._match_cursor < 0)
          this._match_cursor = this._match_history.length
                             ? this._match_history.length - 1
                             : 0;

        this._set_filter_value();
        return false;

    }
  };

  this._set_filter_value = function()
  {
    if (this._match_history[this._match_cursor] && this._input)
    {
      this._input.value = this._match_history[this._match_cursor];
      this._input.selectionStart = 0;
      this._input.selectionEnd = this._input.value.length;
      this._filter.search(this._input.value);
      this._update_clear_button_state();
    }
  };

  this._onbeforesearch = function(msg)
  {
    if (this._script_list)
    {
      var tmpl = templates.script_dropdown_options(this._id,
                                                   this._runtimes,
                                                   _stopped_script_id,
                                                   this._selected_script,
                                                   msg.search_term);
      this._script_list.clearAndRender(tmpl);
      this._get_option_ele_list();
    }
  };

  this._get_option_ele_list = function()
  {
    var list = this._script_list.querySelectorAll("cst-option");
    this._option_eles = Array.prototype.slice.call(list);
    var index = 0;

    if (this._option_eles.length)
    {
      for (var opt; opt = this._option_eles[index]; index++)
      {
        if (opt.hasClass("selected"))
          break;
      }

      if (!this._option_eles[index])
        index = 0;

      this._option_eles[index].addClass("hover");
    }

    this._option_ele_cursor = index;
    this._option_box = this._container
                     ? this._container.getBoundingClientRect()
                     : null;
  };

  this._onclearfilter = function(event, target)
  {
    this._filter.searchDelayed(this._input.value = "");
    this._update_clear_button_state();
  };

  this._onmouseover = function(event, target)
  {
    var option = event.target.get_ancestor("cst-option");
    var index = this._option_eles.indexOf(option);

    if (index > -1)
      this._move_highlight(index);
  };

  this._move_highlight = function(index)
  {
    if (index < this._option_eles.length)
    {
      if (this._option_eles[this._option_ele_cursor])
        this._option_eles[this._option_ele_cursor].removeClass("hover");

      this._option_ele_cursor = index;
      var ele = this._option_eles[this._option_ele_cursor];
      ele.addClass("hover");
      if (this._option_box)
      {
        var box = ele.getBoundingClientRect();
        if (box.bottom > this._option_box.bottom)
          ele.scrollIntoView();
        else if(box.top < this._option_box.top)
          this._container.scrollTop = 0;
      }
    }
  };

  this._init_match_history = function()
  {
    this._setting = window.settings.js_source;
    this._match_history = this._setting && this._setting.get("js-dd-match-history");
    this._match_cursor = 0;
  }

  this._init = function(id, class_name)
  {
    this.init(id, class_name);
    this.ignore_option_handlers = true;
    this._is_clear_button_visible = false;
    this._filter = new TextSearch(1);
    this._filter.set_query_selector(".js-dd-s-scope");
    this._filter.no_highlight = true;
    this._clear_button = null;
    this._onbeforesearch_bound = this._onbeforesearch.bind(this);
    this._filter.addListener("onbeforesearch", this._onbeforesearch_bound);
    eventHandlers.input[this._id + "-filter"] = this._onfilterinput.bind(this);
    eventHandlers.click["js-dd-clear-filter"] = this._onclearfilter.bind(this);
    eventHandlers.mouseover["js-dd-move-highlight"] = this._onmouseover.bind(this);
    this._onshortcut_bound = this._onshortcut.bind(this);
    var gl_h = ActionBroker.get_instance().get_global_handler();
    gl_h.register_shortcut_listener(this._id + "-filter",
                                    this._onshortcut_bound,
                                    ["highlight-next-match",
                                     "highlight-previous-match",
                                     "show-script",
                                     "next",
                                     "previous"]);
    messages.addListener("thread-stopped-event", onThreadStopped);
    messages.addListener("thread-continue-event", onThreadContinue);
    messages.addListener("application-setup", onApplicationSetup);
    this._tooltip = Tooltips.register("js-script-select", true, false);
    this._setting = null;
    this._match_history = [];
    this._match_cursor = 0;
  }

  this._init(id, class_name);

}

cls.ScriptSelect.prototype = new CstSelect();
<commit_msg>Fix for DFL-2898, Navigating in script drop down with Up key starts to jump if there are a lot of scripts.<commit_after>window.cls || (window.cls = {});

cls.ScriptSelect = function(id, class_name)
{

  var _selected_value = "";
  var _selected_script_id = 0;
  var _stopped_script_id = "";

  this.getSelectedOptionTooltipText = function()
  {
    _selected_script_id = runtimes.getSelectedScript();
    var script = _selected_script_id && runtimes.getScript(_selected_script_id);
    return script && script.script_type == "linked" && script.uri || null;
  }

  this.getSelectedOptionText = function()
  {
    _selected_script_id = runtimes.getSelectedScript();
    if (_selected_script_id)
    {
      var script = runtimes.getScript(_selected_script_id);
      if (script)
      {
        var script_type = script.script_type.capitalize(true);
        return script.uri
             ? script.filename
             : script_type + " – " + (script.script_data.replace(/\s+/g, " ").slice(0, 300) ||
               ui_strings.S_TEXT_ECMA_SCRIPT_SCRIPT_ID + ': ' + script.script_id);
      }
      else if(_selected_script_id == -1)
      {
        return " ";
      }
      else
      {
        opera.postError(ui_strings.S_DRAGONFLY_INFO_MESSAGE +
          'missing script in getSelectedOptionText in cls.ScriptSelect');
      }
    }
    else if(runtimes.getSelectedRuntimeId() &&
            runtimes.isReloadedWindow(runtimes.getActiveWindowId()))
    {
      return ui_strings.S_INFO_RUNTIME_HAS_NO_SCRIPTS;
    }
    return '';
  }

  this.getSelectedOptionValue = function()
  {

  }

  this.templateOptionList = function(select_obj)
  {
    this._runtimes = runtimes.get_dom_runtimes(true);
    this._selected_script = runtimes.getSelectedScript();
    return templates.script_dropdown(this._id,
                                     this._runtimes,
                                     _stopped_script_id,
                                     this._selected_script);
  };

  this.onshowoptionlist = function(container)
  {
    var input = container.querySelector("input");
    if (input)
    {
      this._input = input;
      this._clear_button = container.querySelector(".js-dd-clear-filter");
      this._container = container;
      this._script_list = container.querySelector(".js-dd-script-list");
      this._filter.setContainer(container);
      this._filter.setFormInput(input);
      input.focus();
      this._get_option_ele_list();
      this._update_clear_button_state();
    }
  };

  this.onhideoptionlist = function()
  {
    this._filter.cleanup();
    this._filter.set_search_term("");
    this._input = null;
    this._clear_button = null;
    this._container = null;
    this._option_eles = null;
    this._option_box = null;
    this._option_ele_cursor = 0;
  }

  this.checkChange = function(target_ele)
  {
    var script_id_str = target_ele.get_attr('parent-node-chain', 'script-id');
    var script_id = script_id_str && parseInt(script_id_str);
    if (script_id)
    {
      // TODO is this needed?
      if (script_id != _selected_script_id)
      {
        runtimes.setSelectedScript(script_id);
        topCell.showView(views.js_source.id);
        _selected_script_id = script_id;
      }
      _selected_value = target_ele.textContent;
      return true;
    }
    return false;
  }

  // this.updateElement

  var onThreadStopped = function(msg)
  {
    _stopped_script_id = msg.stop_at.script_id;
  }

  var onThreadContinue = function(msg)
  {
    _stopped_script_id = "";
  }

  var onApplicationSetup = function()
  {
    eventHandlers.change['set-tab-size']({target: {value:  settings.js_source.get('tab-size')}});
  }

  this._onfilterinput = function(event, target)
  {
    this._update_clear_button_state();
    this._filter.searchDelayed(target.value);
  };

  this._update_clear_button_state = function()
  {
    if (this._input && this._clear_button)
    {
      if (this._input.value && !this._is_clear_button_visible)
      {
        this._is_clear_button_visible = true;
        this._clear_button.addClass("js-dd-visible");
      }
      else if (!this._input.value && this._is_clear_button_visible)
      {
        this._is_clear_button_visible = false;
        this._clear_button.removeClass("js-dd-visible");
      }
    }
  };

  this._onshortcut = function(action_id, event, target)
  {
    switch (action_id)
    {
      case "highlight-next-match":
      case "highlight-previous-match":
        var target = this._option_eles[this._option_ele_cursor];
        if (target)
        {
          if (!this._setting)
            this._init_match_history();

          if (this._input && !this._match_history.contains(this._input.value))
          {
            this._match_history.push(this._input.value);
            while (this._match_history.length > 10)
              this._match_history.shift();

            if (this._setting)
              this._setting.set("js-dd-match-history", this._match_history);
          }
          target.dispatchMouseEvent("mouseup");
        }
        return false;

      case "up":
        if (this._option_eles.length > 1)
        {
          var index = this._option_ele_cursor - 1;
          if (index < 0)
            index = this._option_eles.length - 1;

          this._move_highlight(index);
        }
        return false;

      case "down":
        if (this._option_eles.length > 1)
        {
          var index = this._option_ele_cursor + 1;
          if (index > this._option_eles.length - 1)
            index = 0;

          this._move_highlight(index);
        }
        return false;

      case "shift-up":
        if (!this._setting)
          this._init_match_history();

        this._match_cursor++;
        if (this._match_cursor > this._match_history.length - 1)
          this._match_cursor = 0;

        this._set_filter_value();
        return false;

      case "shift-down":
        if (!this._setting)
          this._init_match_history();

        this._match_cursor--;
        if (this._match_cursor < 0)
          this._match_cursor = this._match_history.length
                             ? this._match_history.length - 1
                             : 0;

        this._set_filter_value();
        return false;

    }
  };

  this._set_filter_value = function()
  {
    if (this._match_history[this._match_cursor] && this._input)
    {
      this._input.value = this._match_history[this._match_cursor];
      this._input.selectionStart = 0;
      this._input.selectionEnd = this._input.value.length;
      this._filter.search(this._input.value);
      this._update_clear_button_state();
    }
  };

  this._onbeforesearch = function(msg)
  {
    if (this._script_list)
    {
      var tmpl = templates.script_dropdown_options(this._id,
                                                   this._runtimes,
                                                   _stopped_script_id,
                                                   this._selected_script,
                                                   msg.search_term);
      this._script_list.clearAndRender(tmpl);
      this._get_option_ele_list();
    }
  };

  this._get_option_ele_list = function()
  {
    var list = this._script_list.querySelectorAll("cst-option");
    this._option_eles = Array.prototype.slice.call(list);
    var index = 0;

    if (this._option_eles.length)
    {
      for (var opt; opt = this._option_eles[index]; index++)
      {
        if (opt.hasClass("selected"))
          break;
      }

      if (!this._option_eles[index])
        index = 0;

      this._option_eles[index].addClass("hover");
    }

    this._option_ele_cursor = index;
    this._option_box = this._container
                     ? this._container.getBoundingClientRect()
                     : null;
    if (this._option_box && !this._option_box_delta)
    {
      var style = getComputedStyle(this._container);
      this._option_box_delta = parseInt(style.getPropertyValue("border-top-width"));
    }

  };

  this._onclearfilter = function(event, target)
  {
    this._filter.searchDelayed(this._input.value = "");
    this._update_clear_button_state();
  };

  this._onmouseover = function(event, target)
  {
    var option = event.target.get_ancestor("cst-option");
    var index = this._option_eles.indexOf(option);

    if (index > -1)
      this._move_highlight(index);
  };

  this._move_highlight = function(index)
  {
    if (index < this._option_eles.length)
    {
      if (this._option_eles[this._option_ele_cursor])
        this._option_eles[this._option_ele_cursor].removeClass("hover");

      this._option_ele_cursor = index;
      var ele = this._option_eles[this._option_ele_cursor];
      ele.addClass("hover");
      if (this._option_box)
      {
        var box = ele.getBoundingClientRect();
        if (box.bottom > this._option_box.bottom)
          ele.scrollIntoView();
        else if(box.top < this._option_box.top + this._option_box_delta)
          this._container.scrollTop -= this._option_box.top - box.top
                                     + this._option_box.height - box.height;
      }
    }
  };

  this._init_match_history = function()
  {
    this._setting = window.settings.js_source;
    this._match_history = this._setting && this._setting.get("js-dd-match-history");
    this._match_cursor = 0;
  }

  this._init = function(id, class_name)
  {
    this.init(id, class_name);
    this.ignore_option_handlers = true;
    this._is_clear_button_visible = false;
    this._filter = new TextSearch(1);
    this._filter.set_query_selector(".js-dd-s-scope");
    this._filter.no_highlight = true;
    this._clear_button = null;
    this._onbeforesearch_bound = this._onbeforesearch.bind(this);
    this._filter.addListener("onbeforesearch", this._onbeforesearch_bound);
    eventHandlers.input[this._id + "-filter"] = this._onfilterinput.bind(this);
    eventHandlers.click["js-dd-clear-filter"] = this._onclearfilter.bind(this);
    eventHandlers.mouseover["js-dd-move-highlight"] = this._onmouseover.bind(this);
    this._onshortcut_bound = this._onshortcut.bind(this);
    var gl_h = ActionBroker.get_instance().get_global_handler();
    gl_h.register_shortcut_listener(this._id + "-filter",
                                    this._onshortcut_bound,
                                    ["highlight-next-match",
                                     "highlight-previous-match",
                                     "show-script",
                                     "next",
                                     "previous"]);
    messages.addListener("thread-stopped-event", onThreadStopped);
    messages.addListener("thread-continue-event", onThreadContinue);
    messages.addListener("application-setup", onApplicationSetup);
    this._tooltip = Tooltips.register("js-script-select", true, false);
    this._setting = null;
    this._match_history = [];
    this._match_cursor = 0;
    this._option_box_delta = 0;
  }

  this._init(id, class_name);

}

cls.ScriptSelect.prototype = new CstSelect();
<|endoftext|>


<commit_before>
/*
Tipue Search 5.0
Copyright (c) 2015 Tipue
Tipue Search is released under the MIT License
http://www.tipue.com/search
*/

(function($) {

     $.fn.tipuesearch = function(options) {

          var set = $.extend( {

               'show'                   : 7,
               'newWindow'              : false,
               'showURL'                : true,
               'showTitleCount'         : true,
               'minimumLength'          : 3,
               'descriptiveWords'       : 25,
               'highlightTerms'         : true,
               'highlightEveryTerm'     : false,
               'mode'                   : 'static',
               'liveDescription'        : '*',
               'liveContent'            : '*',
               'contentLocation'        : 'tipuesearch/tipuesearch_content.json',
               'debug'                  : false

          }, options);

          return this.each(function() {

               var tipuesearch_in = {
                    pages: []
               };
               $.ajaxSetup({
                    async: false
               });
               var tipuesearch_t_c = 0;

               if (set.mode == 'live')
               {
                    for (var i = 0; i < tipuesearch_pages.length; i++)
                    {
                         $.get(tipuesearch_pages[i])
                              .done(function(html)
                              {
                                   var cont = $(set.liveContent, html).text();
                                   cont = cont.replace(/\s+/g, ' ');
                                   var desc = $(set.liveDescription, html).text();
                                   desc = desc.replace(/\s+/g, ' ');

                                   var t_1 = html.toLowerCase().indexOf('<title>');
                                   var t_2 = html.toLowerCase().indexOf('</title>', t_1 + 7);
                                   if (t_1 != -1 && t_2 != -1)
                                   {
                                        var tit = html.slice(t_1 + 7, t_2);
                                   }
                                   else
                                   {
                                        var tit = tipuesearch_string_1;
                                   }

                                   tipuesearch_in.pages.push(
                                   {
                                        "title": tit,
                                        "text": desc,
                                        "tags": cont,
                                        "url": tipuesearch_pages[i]
                                   });
                              });
                    }
               }

               if (set.mode == 'json')
               {
                    $.getJSON(set.contentLocation)
                         .done(function(json)
                         {
                              tipuesearch_in = $.extend({}, json);
                         });
               }

               if (set.mode == 'static')
               {
                    tipuesearch_in = $.extend({}, tipuesearch);
               }

               var tipue_search_w = '';
               if (set.newWindow)
               {
                    tipue_search_w = ' target="_blank"';
               }

               function getURLP(name)
               {
                    return decodeURIComponent((new RegExp('[?|&]' + name + '=' + '([^&;]+?)(&|#|;|$)').exec(location.search)||[,""])[1].replace(/\+/g, '%20')) || null;
               }
               if (getURLP('q'))
               {
                    $('#tipue_search_input').val(getURLP('q'));
                    getTipueSearch(0, true);
               }

               $(this).keyup(function(event)
               {
                    if(event.keyCode == '13')
                    {
                         getTipueSearch(0, true);
                    }
               });

               function getTipueSearch(start, replace)
               {
                    $('#tipue_search_content').hide();
                    $('#tipue_search_content').html('

');
                    $('#tipue_search_content').show();

                    var out = '';
                    var results = '';
                    var show_replace = false;
                    var show_stop = false;
                    var standard = true;
                    var c = 0;
                    found = [];

                    var d = $('#tipue_search_input').val().toLowerCase();
                    d = $.trim(d);

                    if ((d.match("^\"") && d.match("\"$")) || (d.match("^'") && d.match("'$")))
                    {
                         standard = false;
                    }

                    if (standard)
                    {
                         var d_w = d.split(' ');
                         d = '';
                         for (var i = 0; i < d_w.length; i++)
                         {
                              var a_w = true;
                              for (var f = 0; f < tipuesearch_stop_words.length; f++)
                              {
                                   if (d_w[i] == tipuesearch_stop_words[f])
                                   {
                                        a_w = false;
                                        show_stop = true;
                                   }
                              }
                              if (a_w)
                              {
                                   d = d + ' ' + d_w[i];
                              }
                         }
                         d = $.trim(d);
                         d_w = d.split(' ');
                    }
                    else
                    {
                         d = d.substring(1, d.length - 1);
                    }

                    if (d.length >= set.minimumLength)
                    {
                         if (standard)
                         {
                              if (replace)
                              {
                                   var d_r = d;
                                   for (var i = 0; i < d_w.length; i++)
                                   {
                                        for (var f = 0; f < tipuesearch_replace.words.length; f++)
                                        {
                                             if (d_w[i] == tipuesearch_replace.words[f].word)
                                             {
                                                  d = d.replace(d_w[i], tipuesearch_replace.words[f].replace_with);
                                                  show_replace = true;
                                             }
                                        }
                                   }
                                   d_w = d.split(' ');
                              }

                              var d_t = d;
                              for (var i = 0; i < d_w.length; i++)
                              {
                                   for (var f = 0; f < tipuesearch_stem.words.length; f++)
                                   {
                                        if (d_w[i] == tipuesearch_stem.words[f].word)
                                        {
                                             d_t = d_t + ' ' + tipuesearch_stem.words[f].stem;
                                        }
                                   }
                              }
                              d_w = d_t.split(' ');

                              for (var i = 0; i < tipuesearch_in.pages.length; i++)
                              {
                                   var score = 0;
                                   var s_t = tipuesearch_in.pages[i].text;
                                   for (var f = 0; f < d_w.length; f++)
                                   {
                                        var pat = new RegExp(d_w[f], 'gi');
                                        if (tipuesearch_in.pages[i].title.search(pat) != -1)
                                        {
                                             var m_c = tipuesearch_in.pages[i].title.match(pat).length;
                                             score += (20 * m_c);
                                        }
                                        if (tipuesearch_in.pages[i].text.search(pat) != -1)
                                        {
                                             var m_c = tipuesearch_in.pages[i].text.match(pat).length;
                                             score += (20 * m_c);
                                        }

                                        if (set.highlightTerms)
                                        {
                                             if (set.highlightEveryTerm)
                                             {
                                                  var patr = new RegExp('(' + d_w[f] + ')', 'gi');
                                             }
                                             else
                                             {
                                                  var patr = new RegExp('(' + d_w[f] + ')', 'i');
                                             }
                                             s_t = s_t.replace(patr, "$1");
                                        }

                                        if (tipuesearch_in.pages[i].tags.search(pat) != -1)
                                        {
                                             var m_c = tipuesearch_in.pages[i].tags.match(pat).length;
                                             score += (10 * m_c);
                                        }

                                        if (tipuesearch_in.pages[i].url.search(pat) != -1)
                                        {
                                             score += 20;
                                        }

                                        if (score != 0)
                                        {
                                             for (var e = 0; e < tipuesearch_weight.weight.length; e++)
                                             {
                                                  if (tipuesearch_in.pages[i].url == tipuesearch_weight.weight[e].url)
                                                  {
                                                       score += tipuesearch_weight.weight[e].score;
                                                  }
                                             }
                                        }

                                        if (d_w[f].match('^-'))
                                        {
                                             pat = new RegExp(d_w[f].substring(1), 'i');
                                             if (tipuesearch_in.pages[i].title.search(pat) != -1 || tipuesearch_in.pages[i].text.search(pat) != -1 || tipuesearch_in.pages[i].tags.search(pat) != -1)
                                             {
                                                  score = 0;
                                             }
                                        }
                                   }

                                   if (score != 0)
                                   {
                                        found.push(
                                        {
                                             "score": score,
                                             "title": tipuesearch_in.pages[i].title,
                                             "desc": s_t,
                                             "url": tipuesearch_in.pages[i].url
                                        });
                                        c++;
                                   }
                              }
                         }
                         else
                         {
                              for (var i = 0; i < tipuesearch_in.pages.length; i++)
                              {
                                   var score = 0;
                                   var s_t = tipuesearch_in.pages[i].text;
                                   var pat = new RegExp(d, 'gi');
                                   if (tipuesearch_in.pages[i].title.search(pat) != -1)
                                   {
                                        var m_c = tipuesearch_in.pages[i].title.match(pat).length;
                                        score += (20 * m_c);
                                   }
                                   if (tipuesearch_in.pages[i].text.search(pat) != -1)
                                   {
                                        var m_c = tipuesearch_in.pages[i].text.match(pat).length;
                                        score += (20 * m_c);
                                   }

                                   if (set.highlightTerms)
                                   {
                                        if (set.highlightEveryTerm)
                                        {
                                             var patr = new RegExp('(' + d + ')', 'gi');
                                        }
                                        else
                                        {
                                             var patr = new RegExp('(' + d + ')', 'i');
                                        }
                                        s_t = s_t.replace(patr, "$1");
                                   }

                                   if (tipuesearch_in.pages[i].tags.search(pat) != -1)
                                   {
                                        var m_c = tipuesearch_in.pages[i].tags.match(pat).length;
                                        score += (10 * m_c);
                                   }

                                   if (tipuesearch_in.pages[i].url.search(pat) != -1)
                                   {
                                        score += 20;
                                   }

                                   if (score != 0)
                                   {
                                        for (var e = 0; e < tipuesearch_weight.weight.length; e++)
                                        {
                                             if (tipuesearch_in.pages[i].url == tipuesearch_weight.weight[e].url)
                                             {
                                                  score += tipuesearch_weight.weight[e].score;
                                             }
                                        }
                                   }

                                   if (score != 0)
                                   {
                                        found.push(
                                        {
                                             "score": score,
                                             "title": tipuesearch_in.pages[i].title,
                                             "desc": s_t,
                                             "url": tipuesearch_in.pages[i].url
                                        });
                                        c++;
                                   }
                              }
                         }

                         if (c != 0)
                         {
                              if (set.showTitleCount && tipuesearch_t_c == 0)
                              {
                                   var title = document.title;
                                   document.title = '(' + c + ') ' + title;
                                   tipuesearch_t_c++;
                              }

                              if (show_replace == 1)
                              {
                                   out += '
' + tipuesearch_string_2 + ' ' + d + '. ' + tipuesearch_string_3 + ' ' + d_r + '
';
                              }
                              if (c == 1)
                              {
                                   out += '
' + tipuesearch_string_4 + '
';
                              }
                              else
                              {
                                   c_c = c.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
                                   out += '
' + c_c + ' ' + tipuesearch_string_5 + '
';
                              }

                              found.sort(function(a, b) { return b.score - a.score } );

                              var l_o = 0;
                              for (var i = 0; i < found.length; i++)
                              {
                                   if (l_o >= start && l_o < set.show + start)
                                   {
                                        out += '
' +  found[i].title + '
';

                                        if (set.debug)
                                        {
                                             out += '
Score: ' + found[i].score + '
';
                                        }

                                        if (set.showURL)
                                        {
                                             var s_u = found[i].url.toLowerCase();
                                             if(s_u.indexOf('http://') == 0)
                                             {
                                                  s_u = s_u.slice(7);
                                             }
                                             out += '
' + s_u + '
';
                                        }

                                        if (found[i].desc)
                                        {
                                             var t = found[i].desc;
                                             var t_d = '';
                                             var t_w = t.split(' ');
                                             if (t_w.length < set.descriptiveWords)
                                             {
                                                  t_d = t;
                                             }
                                             else
                                             {
                                                  for (var f = 0; f < set.descriptiveWords; f++)
                                                  {
                                                       t_d += t_w[f] + ' ';
                                                  }
                                             }
                                             t_d = $.trim(t_d);
                                             if (t_d.charAt(t_d.length - 1) != '.')
                                             {
                                                  t_d += ' ...';
                                             }
                                             out += '
' + t_d + '
';
                                        }
                                   }
                                   l_o++;
                              }

                              if (c > set.show)
                              {
                                   var pages = Math.ceil(c / set.show);
                                   var page = (start / set.show);
                                   out += '

';

                                   if (start > 0)
                                   {
                                       out += '
' + tipuesearch_string_6 + '
';
                                   }

                                   if (page <= 2)
                                   {
                                        var p_b = pages;
                                        if (pages > 3)
                                        {
                                             p_b = 3;
                                        }
                                        for (var f = 0; f < p_b; f++)
                                        {
                                             if (f == page)
                                             {
                                                  out += '
' + (f + 1) + '
';
                                             }
                                             else
                                             {
                                                  out += '
' + (f + 1) + '
';
                                             }
                                        }
                                   }
                                   else
                                   {
                                        var p_b = page + 2;
                                        if (p_b > pages)
                                        {
                                             p_b = pages;
                                        }
                                        for (var f = page - 1; f < p_b; f++)
                                        {
                                             if (f == page)
                                             {
                                                  out += '
' + (f + 1) + '
';
                                             }
                                             else
                                             {
                                                  out += '
' + (f + 1) + '
';
                                             }
                                        }
                                   }

                                   if (page + 1 != pages)
                                   {
                                       out += '
' + tipuesearch_string_7 + '
';
                                   }

                                   out += '

';
                              }
                         }
                         else
                         {
                              out += '
' + tipuesearch_string_8 + '
';
                         }
                    }
                    else
                    {
                         if (show_stop)
                         {
                              out += '
' + tipuesearch_string_8 + '. ' + tipuesearch_string_9 + '
';
                         }
                         else
                         {
                              out += '
' + tipuesearch_string_10 + '
';
                              if (set.minimumLength == 1)
                              {
                                   out += '
' + tipuesearch_string_11 + '
';
                              }
                              else
                              {
                                   out += '
' + tipuesearch_string_12 + ' ' + set.minimumLength + ' ' + tipuesearch_string_13 + '
';
                              }
                         }
                    }

                    $('#tipue_search_content').hide();
                    $('#tipue_search_content').html(out);
                    $('#tipue_search_content').slideDown(200);

                    $('#tipue_search_replaced').click(function()
                    {
                         getTipueSearch(0, false);
                    });

                    $('.tipue_search_foot_box').click(function()
                    {
                         var id_v = $(this).attr('id');
                         var id_a = id_v.split('_');

                         getTipueSearch(parseInt(id_a[0]), id_a[1]);
                    });
               }

          });
     };

})(jQuery);
<commit_msg>Delete tipuesearch.js<commit_after><|endoftext|>


<commit_before>/* Copyright © 2009 Adam Harvey
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

/**
 * Constructs a brightness and contrast filter.
 *
 * @class A brightness and contrast filter.
 * @param {number} brightness A multiplier for the brightness between -1 and 1.
 * @param {number} contrast Contrast multiplier. Low values (0 to 1) will
 *                          result in low contrast, high values (1 to infinity)
 *                          higher contrast. It's probably easiest just to play
 *                          with it.
 */
cinejs.filters.BrightnessContrast = function (brightness, contrast) {
	this.brightness = brightness;
	this.contrast = contrast;
};

/**
 * The main processing function for the brightness and contrast filter.
 *
 * @param {ImageData} frame
 */
cinejs.filters.BrightnessContrast.prototype.processFrame = function (frame) {
	for (var i = 0; i < frame.data.length; i += 4) {
		frame.data[i + 0] = this.processSubpixel(frame.data[i + 0]);
		frame.data[i + 1] = this.processSubpixel(frame.data[i + 1]);
		frame.data[i + 2] = this.processSubpixel(frame.data[i + 2]);
	}
};

/**
 * The processing function applied to the individual red, green and blue
 * subpixels within a frame.
 *
 * @param {number} subpixel
 * @return {number}
 */
cinejs.filters.BrightnessContrast.prototype.processSubpixel = function (subpixel) {
	// Contrast adjustment.
	if (this.contrast != 1) {
		subpixel -= 128;
		subpixel *= this.contrast;
		subpixel += 128;
	}

	// Brightness adjustment.
	subpixel += (255 * this.brightness);

	// Constrain to valid values.
	return Math.min(255, Math.max(0, subpixel));
};

// vim: set cin noet ts=8 sw=8:
<commit_msg>Quiet a lint warning.<commit_after>/* Copyright © 2009 Adam Harvey
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

/**
 * Constructs a brightness and contrast filter.
 *
 * @class A brightness and contrast filter.
 * @param {number} brightness A multiplier for the brightness between -1 and 1.
 * @param {number} contrast Contrast multiplier. Low values (0 to 1) will
 *                          result in low contrast, high values (1 to infinity)
 *                          higher contrast. It's probably easiest just to play
 *                          with it.
 */
cinejs.filters.BrightnessContrast = function (brightness, contrast) {
	this.brightness = brightness;
	this.contrast = contrast;
};

/**
 * The main processing function for the brightness and contrast filter.
 *
 * @param {ImageData} frame
 */
cinejs.filters.BrightnessContrast.prototype.processFrame = function (frame) {
	for (var i = 0; i < frame.data.length; i += 4) {
		frame.data[i + 0] = this.processSubpixel(frame.data[i + 0]);
		frame.data[i + 1] = this.processSubpixel(frame.data[i + 1]);
		frame.data[i + 2] = this.processSubpixel(frame.data[i + 2]);
	}
};

/**
 * The processing function applied to the individual red, green and blue
 * subpixels within a frame.
 *
 * @param {number} subpixel
 * @return {number}
 */
cinejs.filters.BrightnessContrast.prototype.processSubpixel = function (subpixel) {
	// Contrast adjustment.
	if (this.contrast !== 1) {
		subpixel -= 128;
		subpixel *= this.contrast;
		subpixel += 128;
	}

	// Brightness adjustment.
	subpixel += (255 * this.brightness);

	// Constrain to valid values.
	return Math.min(255, Math.max(0, subpixel));
};

// vim: set cin noet ts=8 sw=8:
<|endoftext|>


<commit_before>/* eslint global-require: 0 */

import _ from 'underscore'

import NylasStore from 'nylas-store'
import KeyManager from '../../key-manager'
import Actions from '../actions'
import Account from '../models/account'
import Utils from '../models/utils'
import DatabaseStore from './database-store'

const configAccountsKey = "nylas.accounts"
const configVersionKey = "nylas.accountsVersion"

/*
Public: The AccountStore listens to changes to the available accounts in
the database and exposes the currently active Account via {::current}

Section: Stores
*/
class AccountStore extends NylasStore {

  constructor(props) {
    super(props)
    this._loadAccounts()
    this.listenTo(Actions.removeAccount, this._onRemoveAccount)
    this.listenTo(Actions.updateAccount, this._onUpdateAccount)
    this.listenTo(Actions.reorderAccount, this._onReorderAccount)

    NylasEnv.config.onDidChange(configVersionKey, async (change) => {
      // If we already have this version of the accounts config, it means we
      // are the ones who saved the change, and we don't need to reload.
      if (this._version / 1 === change.newValue / 1) {
        return;
      }

      const oldAccountIds = _.pluck(this._accounts, 'id')
      this._loadAccounts()
      const accountIds = _.pluck(this._accounts, 'id')
      const newAccountIds = _.difference(accountIds, oldAccountIds)

      if (NylasEnv.isMainWindow() && newAccountIds.length > 0) {
        const newId = newAccountIds[0]
        // const NylasSyncStatusStore = require('./nylas-sync-status-store').default
        Actions.focusDefaultMailboxPerspectiveForAccounts([newId], {sidebarAccountIds: accountIds})
        // TODO:
        // Wait for a little bit before uncollapsong the sidebar to show the
        // newly focused inbox
        // This Action is a hack, get rid of it in sidebar refactor
        // Wait until the FocusedPerspectiveStore triggers and the sidebar is
        // updated to uncollapse the inbox for the new account
        const NylasSyncStatusStore = require('./nylas-sync-status-store').default
        await NylasSyncStatusStore.whenCategoryListSynced(newId)
        Actions.focusDefaultMailboxPerspectiveForAccounts([newId], {sidebarAccountIds: accountIds})
        Actions.setCollapsedSidebarItem('Inbox', false)
      }
    })
  }

  _loadAccounts = () => {
    try {
      this._caches = {}
      this._tokens = {}
      this._version = NylasEnv.config.get(configVersionKey) || 0

      this._accounts = []
      for (const json of NylasEnv.config.get(configAccountsKey) || []) {
        this._accounts.push((new Account()).fromJSON(json))
      }

      // Run a few checks on account consistency. We want to display useful error
      // messages and these can result in very strange exceptions downstream otherwise.
      this._enforceAccountsValidity()

      // Load tokens using the new KeyManager method
      this._tokens = {}
      for (const account of this._accounts) {
        const credentials = {
          n1Cloud: KeyManager.getPassword(`${account.emailAddress}.n1Cloud`, {migrateFromService: "Nylas"}),
          localSync: KeyManager.getPassword(`${account.emailAddress}.localSync`, {migrateFromService: "Nylas"}),
        }
        this._tokens[account.id] = credentials;

        // TODO HACK. For some reason we're getting passed the wrong
        // id. Figure this out after launch.
        this._tokens[account.emailAddress] = credentials;
      }
    } catch (error) {
      NylasEnv.reportError(error)
    }

    this._trigger()
  }

  _enforceAccountsValidity = () => {
    const seenIds = {}
    const seenEmails = {}
    let message = null

    this._accounts = this._accounts.filter((account) => {
      if (!account.emailAddress) {
        message = "Assertion failure: One of the accounts in config.json did not have an emailAddress, and was removed. You should re-link the account."
        return false
      }
      if (seenIds[account.id]) {
        message = "Assertion failure: Two of the accounts in config.json had the same ID and one was removed. Please give each account a separate ID."
        return false
      }
      if (seenEmails[account.emailAddress]) {
        message = "Assertion failure: Two of the accounts in config.json had the same email address and one was removed."
        return false
      }

      seenIds[account.id] = true
      seenEmails[account.emailAddress] = true
      return true
    })

    if (message && NylasEnv.isMainWindow()) {
      NylasEnv.showErrorDialog(`N1 was unable to load your account preferences.\n\n${message}`)
    }
  }

  _trigger() {
    for (const account of this._accounts) {
      if (!account || !account.id) {
        const err = new Error("An invalid account was added to `this._accounts`")
        NylasEnv.reportError(err)
        this._accounts = _.compact(this._accounts)
      }
    }
    this.trigger()
  }

  _save = () => {
    this._version += 1
    const configAccounts = this._accounts.map(a => a.toJSON())
    configAccounts.forEach(a => delete a.sync_error)
    NylasEnv.config.set(configAccountsKey, configAccounts)
    NylasEnv.config.set(configVersionKey, this._version)
    this._trigger()
  }

  /**
   * Actions.updateAccount is called directly from the local-sync worker.
   * This will update the account with its updated sync state
   */
  _onUpdateAccount = (id, updated) => {
    const idx = _.findIndex(this._accounts, (a) => a.id === id)
    let account = this._accounts[idx]
    if (!account) return
    account = _.extend(account, updated)
    this._caches = {}
    this._accounts[idx] = account
    this._save()
  }

  /**
   * When an account is removed from Nylas Mail, the AccountStore
   * triggers. The local-sync/src/local-sync-worker/index.js listens to
   * the AccountStore and runs `ensureK2Consistency`. This will actually
   * delete the Account on the local sync side.
   */
  _onRemoveAccount = (id) => {
    const account = _.findWhere(this._accounts, {id})
    if (!account) return
    KeyManager.deletePassword(account.emailAddress)

    this._caches = {}

    const remainingAccounts = _.without(this._accounts, account)
    // This action is called before saving because we need to unfocus the
    // perspective of the account that is being removed before removing the
    // account, otherwise when we trigger with the new set of accounts, the
    // current perspective will still reference a stale accountId which will
    // cause things to break
    Actions.focusDefaultMailboxPerspectiveForAccounts(remainingAccounts)
    _.defer(() => {
      Actions.setCollapsedSidebarItem('Inbox', true)
    })

    this._accounts = remainingAccounts
    this._save()

    if (remainingAccounts.length === 0) {
      const ipc = require('electron').ipcRenderer
      ipc.send('command', 'application:relaunch-to-initial-windows', {
        resetDatabase: true,
      })
    }
  }

  _onReorderAccount = (id, newIdx) => {
    const existingIdx = _.findIndex(this._accounts, (a) => a.id === id)
    if (existingIdx === -1) return
    const account = this._accounts[existingIdx]
    this._caches = {}
    this._accounts.splice(existingIdx, 1)
    this._accounts.splice(newIdx, 0, account)
    this._save()
  }

  addAccountFromJSON = (json, localToken, cloudToken) => {
    if (!json.email_address || !json.provider) {
      console.error("Returned account data is invalid", json)
      console.log(JSON.stringify(json))
      throw new Error("Returned account data is invalid")
    }

    this._loadAccounts()

    this._tokens[json.id] = {
      n1Cloud: cloudToken,
      localSync: localToken,
    }
    KeyManager.replacePassword(`${json.email_address}.n1Cloud`, cloudToken)
    KeyManager.replacePassword(`${json.email_address}.localSync`, localToken)

    const existingIdx = _.findIndex(this._accounts, (a) =>
      a.id === json.id || a.emailAddress === json.email_address
    )

    if (existingIdx === -1) {
      const account = (new Account()).fromJSON(json)
      this._accounts.push(account)
    } else {
      const account = this._accounts[existingIdx]
      account.syncState = Account.SYNC_STATE_RUNNING
      account.fromJSON(json)
    }

    this._save()
  }

  _cachedGetter(key, fn) {
    this._caches[key] = this._caches[key] || fn()
    return this._caches[key]
  }

  // Public: Returns an {Array} of {Account} objects
  accounts = () => {
    return this._accounts
  }

  accountIds = () => {
    return _.pluck(this._accounts, 'id')
  }

  accountsForItems = (items) => {
    const accounts = {}
    items.forEach(({accountId}) => {
      accounts[accountId] = accounts[accountId] || this.accountForId(accountId)
    })
    return _.values(accounts)
  }

  accountForItems = (items) => {
    const accounts = this.accountsForItems(items)
    if (accounts.length > 1) return null
    return accounts[0]
  }

  // Public: Returns the {Account} for the given email address, or null.
  accountForEmail = (email) => {
    for (const account of this.accounts()) {
      if (Utils.emailIsEquivalent(email, account.emailAddress)) {
        return account
      }
    }
    for (const alias of this.aliases()) {
      if (Utils.emailIsEquivalent(email, alias.email)) {
        return this.accountForId(alias.accountId)
      }
    }
    return null
  }

  // Public: Returns the {Account} for the given account id, or null.
  accountForId(id) {
    return this._cachedGetter(`accountForId:${id}`, () => _.findWhere(this._accounts, {id}))
  }

  accountIsSyncing(accountId) {
    const account = this.accountForId(accountId)
    return !account.hasSyncStateError()
  }

  accountsAreSyncing() {
    return this.accounts().every(acc => !acc.hasSyncStateError())
  }

  emailAddresses() {
    let addresses = _.pluck((this.accounts() ? this.accounts() : []), "emailAddress")
    addresses = addresses.concat(_.pluck((this.aliases() ? this.aliases() : []), "email"))
    return _.unique(addresses)
  }

  aliases() {
    return this._cachedGetter("aliases", () => {
      const aliases = []
      for (const acc of this._accounts) {
        aliases.push(acc.me())
        for (const alias of acc.aliases) {
          const aliasContact = acc.meUsingAlias(alias)
          aliasContact.isAlias = true
          aliases.push(aliasContact)
        }
      }
      return aliases
    })
  }

  aliasesFor(accountsOrIds) {
    const ids = accountsOrIds.map((accOrId) => {
      return accOrId instanceof Account ? accOrId.id : accOrId
    })
    return this.aliases().filter((contact) => ids.includes(contact.accountId))
  }

  // Public: Returns the currently active {Account}.
  current() {
    throw new Error("AccountStore.current() has been deprecated.")
  }

  // Private: This method is going away soon, do not rely on it.
  tokensForAccountId(id) {
    return this._tokens[id]
  }

  // Private: Load fake data from a directory for taking nice screenshots
  _importFakeData = (dir) => {
    const fs = require('fs-plus')
    const path = require('path')
    const Message = require('../models/message').default
    const Thread = require('../models/thread').default

    this._caches = {}

    let labels = []
    const threads = []
    let messages = []

    let account = this.accountForEmail('nora@nylas.com')
    if (!account) {
      account = new Account()
      account.serverId = account.clientId
      account.emailAddress = "nora@nylas.com"
      account.organizationUnit = 'label'
      account.label = "Nora's Email"
      account.aliases = []
      account.name = "Nora"
      account.provider = "gmail"
      const json = account.toJSON()
      json.token = 'nope'
      this.addAccountFromJSON(json)
    }

    const filenames = fs.readdirSync(path.join(dir, 'threads'))
    for (const filename of filenames) {
      const threadJSON = fs.readFileSync(path.join(dir, 'threads', filename))
      const threadMessages = JSON.parse(threadJSON).map((j) => (new Message()).fromJSON(j))
      let threadLabels = []
      let threadParticipants = []
      let threadAttachment = false
      let threadUnread = false

      for (const m of threadMessages) {
        m.accountId = account.id
        for (const l of m.categories) {
          l.accountId = account.id
        }
        for (const l of m.files) {
          l.accountId = account.id
        }
        threadParticipants = threadParticipants.concat(m.participants())
        threadLabels = threadLabels.concat(m.categories)
        threadAttachment = threadAttachment || m.files.length > 0
        threadUnread = threadUnread || m.unread
      }

      threadParticipants = _.uniq(threadParticipants, (p) => p.email)
      threadLabels = _.uniq(threadLabels, (l) => l.id)
      labels = _.uniq(labels.concat(threadLabels), (l) => l.id)

      const lastMsg = _.last(threadMessages)
      const thread = new Thread({
        accountId: account.id,
        serverId: lastMsg.threadId,
        clientId: lastMsg.threadId,
        subject: lastMsg.subject,
        lastMessageReceivedTimestamp: lastMsg.date,
        hasAttachment: threadAttachment,
        categories: threadLabels,
        participants: threadParticipants,
        unread: threadUnread,
        snippet: lastMsg.snippet,
        starred: lastMsg.starred,
      })
      messages = messages.concat(threadMessages)
      threads.push(thread)
    }

    const downloadsDir = path.join(dir, 'downloads')
    if (fs.existsSync(downloadsDir)) {
      for (const filename of fs.readdirSync(downloadsDir)) {
        const destPath = path.join(NylasEnv.getConfigDirPath(), 'downloads', filename)
        if (fs.existsSync(destPath)) {
          fs.removeSync(destPath)
        }
        fs.copySync(path.join(downloadsDir, filename), destPath)
      }
    }

    DatabaseStore.inTransaction((t) =>
      Promise.all([
        t.persistModel(account),
        t.persistModels(labels),
        t.persistModels(messages),
        t.persistModels(threads),
      ])
    ).then(() => {
      Actions.focusDefaultMailboxPerspectiveForAccounts([account.id])
    })
    .then(() => {
      return new Promise((resolve) => setTimeout(resolve, 1000))
    })
  }
}

export default new AccountStore()
<commit_msg>[account-store] Don't `getPassword` if we don't have to<commit_after>/* eslint global-require: 0 */

import _ from 'underscore'

import NylasStore from 'nylas-store'
import KeyManager from '../../key-manager'
import Actions from '../actions'
import Account from '../models/account'
import Utils from '../models/utils'
import DatabaseStore from './database-store'

const configAccountsKey = "nylas.accounts"
const configVersionKey = "nylas.accountsVersion"

/*
Public: The AccountStore listens to changes to the available accounts in
the database and exposes the currently active Account via {::current}

Section: Stores
*/
class AccountStore extends NylasStore {

  constructor(props) {
    super(props)
    this._loadAccounts()
    this.listenTo(Actions.removeAccount, this._onRemoveAccount)
    this.listenTo(Actions.updateAccount, this._onUpdateAccount)
    this.listenTo(Actions.reorderAccount, this._onReorderAccount)

    NylasEnv.config.onDidChange(configVersionKey, async (change) => {
      // If we already have this version of the accounts config, it means we
      // are the ones who saved the change, and we don't need to reload.
      if (this._version / 1 === change.newValue / 1) {
        return;
      }

      const oldAccountIds = _.pluck(this._accounts, 'id')
      this._loadAccounts()
      const accountIds = _.pluck(this._accounts, 'id')
      const newAccountIds = _.difference(accountIds, oldAccountIds)

      if (NylasEnv.isMainWindow() && newAccountIds.length > 0) {
        const newId = newAccountIds[0]
        // const NylasSyncStatusStore = require('./nylas-sync-status-store').default
        Actions.focusDefaultMailboxPerspectiveForAccounts([newId], {sidebarAccountIds: accountIds})
        // TODO:
        // Wait for a little bit before uncollapsong the sidebar to show the
        // newly focused inbox
        // This Action is a hack, get rid of it in sidebar refactor
        // Wait until the FocusedPerspectiveStore triggers and the sidebar is
        // updated to uncollapse the inbox for the new account
        const NylasSyncStatusStore = require('./nylas-sync-status-store').default
        await NylasSyncStatusStore.whenCategoryListSynced(newId)
        Actions.focusDefaultMailboxPerspectiveForAccounts([newId], {sidebarAccountIds: accountIds})
        Actions.setCollapsedSidebarItem('Inbox', false)
      }
    })
  }

  _loadAccounts = () => {
    try {
      this._caches = {}
      this._tokens = this._tokens || {};
      this._version = NylasEnv.config.get(configVersionKey) || 0

      const oldAccountIds = _.pluck(this._accounts, 'id')
      this._accounts = []
      for (const json of NylasEnv.config.get(configAccountsKey) || []) {
        this._accounts.push((new Account()).fromJSON(json))
      }
      const accountIds = _.pluck(this._accounts, 'id')

      // Loading passwords from the KeyManager is expensive so only do it if
      // we really have to (i.e. we're loading a new Account)
      const addedAccountIds = _.difference(accountIds, oldAccountIds);
      const addedAccounts = _.filter(this._accounts, (a) => addedAccountIds.includes(a.id));

      const removedAccountIds = _.difference(oldAccountIds, accountIds);
      if (removedAccountIds.length > 0) {
        NylasEnv.reportError(new Error('_loadAccounts encountered Accounts that should have already been removed'));
      }

      // Run a few checks on account consistency. We want to display useful error
      // messages and these can result in very strange exceptions downstream otherwise.
      this._enforceAccountsValidity()

      for (const account of addedAccounts) {
        const credentials = {
          n1Cloud: KeyManager.getPassword(`${account.emailAddress}.n1Cloud`, {migrateFromService: "Nylas"}),
          localSync: KeyManager.getPassword(`${account.emailAddress}.localSync`, {migrateFromService: "Nylas"}),
        }
        this._tokens[account.id] = credentials;

        // TODO HACK. For some reason we're getting passed the wrong
        // id. Figure this out after launch.
        this._tokens[account.emailAddress] = credentials;
      }
    } catch (error) {
      NylasEnv.reportError(error)
    }

    this._trigger()
  }

  _enforceAccountsValidity = () => {
    const seenIds = {}
    const seenEmails = {}
    let message = null

    this._accounts = this._accounts.filter((account) => {
      if (!account.emailAddress) {
        message = "Assertion failure: One of the accounts in config.json did not have an emailAddress, and was removed. You should re-link the account."
        return false
      }
      if (seenIds[account.id]) {
        message = "Assertion failure: Two of the accounts in config.json had the same ID and one was removed. Please give each account a separate ID."
        return false
      }
      if (seenEmails[account.emailAddress]) {
        message = "Assertion failure: Two of the accounts in config.json had the same email address and one was removed."
        return false
      }

      seenIds[account.id] = true
      seenEmails[account.emailAddress] = true
      return true
    })

    if (message && NylasEnv.isMainWindow()) {
      NylasEnv.showErrorDialog(`N1 was unable to load your account preferences.\n\n${message}`)
    }
  }

  _trigger() {
    for (const account of this._accounts) {
      if (!account || !account.id) {
        const err = new Error("An invalid account was added to `this._accounts`")
        NylasEnv.reportError(err)
        this._accounts = _.compact(this._accounts)
      }
    }
    this.trigger()
  }

  _save = () => {
    this._version += 1
    const configAccounts = this._accounts.map(a => a.toJSON())
    configAccounts.forEach(a => delete a.sync_error)
    NylasEnv.config.set(configAccountsKey, configAccounts)
    NylasEnv.config.set(configVersionKey, this._version)
    this._trigger()
  }

  /**
   * Actions.updateAccount is called directly from the local-sync worker.
   * This will update the account with its updated sync state
   */
  _onUpdateAccount = (id, updated) => {
    const idx = _.findIndex(this._accounts, (a) => a.id === id)
    let account = this._accounts[idx]
    if (!account) return
    account = _.extend(account, updated)
    this._caches = {}
    this._accounts[idx] = account
    this._save()
  }

  /**
   * When an account is removed from Nylas Mail, the AccountStore
   * triggers. The local-sync/src/local-sync-worker/index.js listens to
   * the AccountStore and runs `ensureK2Consistency`. This will actually
   * delete the Account on the local sync side.
   */
  _onRemoveAccount = (id) => {
    const account = _.findWhere(this._accounts, {id})
    if (!account) return
    KeyManager.deletePassword(account.emailAddress)

    this._caches = {}

    const remainingAccounts = _.without(this._accounts, account)
    // This action is called before saving because we need to unfocus the
    // perspective of the account that is being removed before removing the
    // account, otherwise when we trigger with the new set of accounts, the
    // current perspective will still reference a stale accountId which will
    // cause things to break
    Actions.focusDefaultMailboxPerspectiveForAccounts(remainingAccounts)
    _.defer(() => {
      Actions.setCollapsedSidebarItem('Inbox', true)
    })

    this._accounts = remainingAccounts
    this._save()

    if (remainingAccounts.length === 0) {
      const ipc = require('electron').ipcRenderer
      ipc.send('command', 'application:relaunch-to-initial-windows', {
        resetDatabase: true,
      })
    }
  }

  _onReorderAccount = (id, newIdx) => {
    const existingIdx = _.findIndex(this._accounts, (a) => a.id === id)
    if (existingIdx === -1) return
    const account = this._accounts[existingIdx]
    this._caches = {}
    this._accounts.splice(existingIdx, 1)
    this._accounts.splice(newIdx, 0, account)
    this._save()
  }

  addAccountFromJSON = (json, localToken, cloudToken) => {
    if (!json.email_address || !json.provider) {
      console.error("Returned account data is invalid", json)
      console.log(JSON.stringify(json))
      throw new Error("Returned account data is invalid")
    }

    this._loadAccounts()

    this._tokens[json.id] = {
      n1Cloud: cloudToken,
      localSync: localToken,
    }
    KeyManager.replacePassword(`${json.email_address}.n1Cloud`, cloudToken)
    KeyManager.replacePassword(`${json.email_address}.localSync`, localToken)

    const existingIdx = _.findIndex(this._accounts, (a) =>
      a.id === json.id || a.emailAddress === json.email_address
    )

    if (existingIdx === -1) {
      const account = (new Account()).fromJSON(json)
      this._accounts.push(account)
    } else {
      const account = this._accounts[existingIdx]
      account.syncState = Account.SYNC_STATE_RUNNING
      account.fromJSON(json)
    }

    this._save()
  }

  _cachedGetter(key, fn) {
    this._caches[key] = this._caches[key] || fn()
    return this._caches[key]
  }

  // Public: Returns an {Array} of {Account} objects
  accounts = () => {
    return this._accounts
  }

  accountIds = () => {
    return _.pluck(this._accounts, 'id')
  }

  accountsForItems = (items) => {
    const accounts = {}
    items.forEach(({accountId}) => {
      accounts[accountId] = accounts[accountId] || this.accountForId(accountId)
    })
    return _.values(accounts)
  }

  accountForItems = (items) => {
    const accounts = this.accountsForItems(items)
    if (accounts.length > 1) return null
    return accounts[0]
  }

  // Public: Returns the {Account} for the given email address, or null.
  accountForEmail = (email) => {
    for (const account of this.accounts()) {
      if (Utils.emailIsEquivalent(email, account.emailAddress)) {
        return account
      }
    }
    for (const alias of this.aliases()) {
      if (Utils.emailIsEquivalent(email, alias.email)) {
        return this.accountForId(alias.accountId)
      }
    }
    return null
  }

  // Public: Returns the {Account} for the given account id, or null.
  accountForId(id) {
    return this._cachedGetter(`accountForId:${id}`, () => _.findWhere(this._accounts, {id}))
  }

  accountIsSyncing(accountId) {
    const account = this.accountForId(accountId)
    return !account.hasSyncStateError()
  }

  accountsAreSyncing() {
    return this.accounts().every(acc => !acc.hasSyncStateError())
  }

  emailAddresses() {
    let addresses = _.pluck((this.accounts() ? this.accounts() : []), "emailAddress")
    addresses = addresses.concat(_.pluck((this.aliases() ? this.aliases() : []), "email"))
    return _.unique(addresses)
  }

  aliases() {
    return this._cachedGetter("aliases", () => {
      const aliases = []
      for (const acc of this._accounts) {
        aliases.push(acc.me())
        for (const alias of acc.aliases) {
          const aliasContact = acc.meUsingAlias(alias)
          aliasContact.isAlias = true
          aliases.push(aliasContact)
        }
      }
      return aliases
    })
  }

  aliasesFor(accountsOrIds) {
    const ids = accountsOrIds.map((accOrId) => {
      return accOrId instanceof Account ? accOrId.id : accOrId
    })
    return this.aliases().filter((contact) => ids.includes(contact.accountId))
  }

  // Public: Returns the currently active {Account}.
  current() {
    throw new Error("AccountStore.current() has been deprecated.")
  }

  // Private: This method is going away soon, do not rely on it.
  tokensForAccountId(id) {
    return this._tokens[id]
  }

  // Private: Load fake data from a directory for taking nice screenshots
  _importFakeData = (dir) => {
    const fs = require('fs-plus')
    const path = require('path')
    const Message = require('../models/message').default
    const Thread = require('../models/thread').default

    this._caches = {}

    let labels = []
    const threads = []
    let messages = []

    let account = this.accountForEmail('nora@nylas.com')
    if (!account) {
      account = new Account()
      account.serverId = account.clientId
      account.emailAddress = "nora@nylas.com"
      account.organizationUnit = 'label'
      account.label = "Nora's Email"
      account.aliases = []
      account.name = "Nora"
      account.provider = "gmail"
      const json = account.toJSON()
      json.token = 'nope'
      this.addAccountFromJSON(json)
    }

    const filenames = fs.readdirSync(path.join(dir, 'threads'))
    for (const filename of filenames) {
      const threadJSON = fs.readFileSync(path.join(dir, 'threads', filename))
      const threadMessages = JSON.parse(threadJSON).map((j) => (new Message()).fromJSON(j))
      let threadLabels = []
      let threadParticipants = []
      let threadAttachment = false
      let threadUnread = false

      for (const m of threadMessages) {
        m.accountId = account.id
        for (const l of m.categories) {
          l.accountId = account.id
        }
        for (const l of m.files) {
          l.accountId = account.id
        }
        threadParticipants = threadParticipants.concat(m.participants())
        threadLabels = threadLabels.concat(m.categories)
        threadAttachment = threadAttachment || m.files.length > 0
        threadUnread = threadUnread || m.unread
      }

      threadParticipants = _.uniq(threadParticipants, (p) => p.email)
      threadLabels = _.uniq(threadLabels, (l) => l.id)
      labels = _.uniq(labels.concat(threadLabels), (l) => l.id)

      const lastMsg = _.last(threadMessages)
      const thread = new Thread({
        accountId: account.id,
        serverId: lastMsg.threadId,
        clientId: lastMsg.threadId,
        subject: lastMsg.subject,
        lastMessageReceivedTimestamp: lastMsg.date,
        hasAttachment: threadAttachment,
        categories: threadLabels,
        participants: threadParticipants,
        unread: threadUnread,
        snippet: lastMsg.snippet,
        starred: lastMsg.starred,
      })
      messages = messages.concat(threadMessages)
      threads.push(thread)
    }

    const downloadsDir = path.join(dir, 'downloads')
    if (fs.existsSync(downloadsDir)) {
      for (const filename of fs.readdirSync(downloadsDir)) {
        const destPath = path.join(NylasEnv.getConfigDirPath(), 'downloads', filename)
        if (fs.existsSync(destPath)) {
          fs.removeSync(destPath)
        }
        fs.copySync(path.join(downloadsDir, filename), destPath)
      }
    }

    DatabaseStore.inTransaction((t) =>
      Promise.all([
        t.persistModel(account),
        t.persistModels(labels),
        t.persistModels(messages),
        t.persistModels(threads),
      ])
    ).then(() => {
      Actions.focusDefaultMailboxPerspectiveForAccounts([account.id])
    })
    .then(() => {
      return new Promise((resolve) => setTimeout(resolve, 1000))
    })
  }
}

export default new AccountStore()
<|endoftext|>


<commit_before>/**
 *
 *  Web Starter Kit
 *  Copyright 2014 Google Inc. All rights reserved.
 *
 *  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
 *
 */

'use strict';

// Include Gulp & Tools We'll Use
var gulp = require('gulp');
var $ = require('gulp-load-plugins')();
var del = require('del');
var runSequence = require('run-sequence');
var browserSync = require('browser-sync');
var pagespeed = require('psi');
var reload = browserSync.reload;

var AUTOPREFIXER_BROWSERS = [
  'ie >= 10',
  'ie_mob >= 10',
  'ff >= 30',
  'chrome >= 34',
  'safari >= 7',
  'opera >= 23',
  'ios >= 7',
  'android >= 4.4',
  'bb >= 10'
];

// Lint JavaScript
gulp.task('jshint', function () {
  return gulp.src('app/scripts/**/*.js')
    .pipe(reload({stream: true, once: true}))
    .pipe($.jshint())
    .pipe($.jshint.reporter('jshint-stylish'))
    .pipe($.if(!browserSync.active, $.jshint.reporter('fail')));
});

// Optimize Images
gulp.task('images', function () {
  return gulp.src('app/images/**/*')
    .pipe($.cache($.imagemin({
      progressive: true,
      interlaced: true
    })))
    .pipe(gulp.dest('dist/images'))
    .pipe($.size({title: 'images'}));
});

// Copy All Files At The Root Level (app)
gulp.task('copy', function () {
  return gulp.src([
    'app/*',
    '!app/*.html',

  ], {
    dot: true
  }).pipe(gulp.dest('dist'))
    .pipe($.size({title: 'copy'}));
});

// Copy Web Fonts To Dist
gulp.task('fonts', function () {
  return gulp.src(['app/fonts/**'])
    .pipe(gulp.dest('dist/fonts'))
    .pipe($.size({title: 'fonts'}));
});

// Compile and Automatically Prefix Stylesheets
gulp.task('styles', function () {
  // For best performance, don't add Sass partials to `gulp.src`
  return gulp.src([
    'app/styles/*.scss',
    'app/styles/**/*.css',
    'app/styles/components/components.scss'
  ])
    .pipe($.changed('styles', {extension: '.scss'}))
    .pipe($.rubySass({
      style: 'expanded',
      precision: 10
    }))
    .on('error', console.error.bind(console))
    .pipe($.autoprefixer({browsers: AUTOPREFIXER_BROWSERS}))
    .pipe(gulp.dest('.tmp/styles'))
    // Concatenate And Minify Styles
    .pipe($.if('*.css', $.csso()))
    .pipe(gulp.dest('dist/styles'))
    .pipe($.size({title: 'styles'}));
});

// Scan Your HTML For Assets & Optimize Them
gulp.task('html', function () {
  var assets = $.useref.assets({searchPath: '{.tmp,app}'});

  return gulp.src('app/_includes/*.html')
    .pipe(assets)
    // Concatenate And Minify JavaScript
    .pipe($.if('*.js', $.uglify({preserveComments: 'some'})))
    // Remove Any Unused CSS
    // Note: If not using the Style Guide, you can delete it from
    // the next line to only include styles your project uses.
    // .pipe($.if('*.css', $.uncss({
    //   html: [
    //     'app/index.html'
    //     // 'app/styleguide.html'
    //   ],
    //   // CSS Selectors for UnCSS to ignore
    //   // ignore: [
    //   //   /.side-nav.open/,
    //   //   /.header-bar.open/
    //   // ]
    // })))
    // Concatenate And Minify Styles
    // In case you are still using useref build blocks
    .pipe($.if('*.css', $.csso()))
    .pipe(assets.restore())
    .pipe($.useref())
    // Update Production Style Guide Paths
    .pipe($.replace('components/components.css', 'components/main.min.css'))
    // Minify Any HTML
    .pipe($.if('*.html', $.minifyHtml()))
    // Output Files
    .pipe(gulp.dest('dist'))
    .pipe($.size({title: 'html'}));
});

// Static assets revisioning: unicorn.css → unicorn-098f6bcd.css
gulp.task('rev', function () {
  return gulp.src(['dist/styles/*.css', 'dist/scripts/*.js'], {base: 'dist'})
    .pipe(gulp.dest('dist'))
    .pipe($.rev())
    .pipe(gulp.dest('dist'))
    .pipe($.rev.manifest('dist/rev-manifest.json', {base: 'dist'}))
    .pipe(gulp.dest('dist'));
});

// Rewrite occurences of filenames which have been renamed by 'rev' task
gulp.task('rev-replace', function () {
  var manifest = gulp.src('dist/rev-manifest.json');
  return gulp.src('dist/*.html')
    .pipe($.revReplace({manifest: manifest}))
    .pipe(gulp.dest('dist'));
});

// Clean Output Directory
gulp.task('clean', del.bind(null, ['.tmp', 'dist/*', '!dist/.git']));

// Watch Files For Changes & Reload
gulp.task('serve', ['styles'], function () {
  browserSync({
    notify: false,
    // Run as an https by uncommenting 'https: true'
    // Note: this uses an unsigned certificate which on first access
    //       will present a certificate warning in the browser.
    // https: true,
    server: ['.tmp', 'app']
  });

  gulp.watch(['app/**/*.html'], reload);
  gulp.watch(['app/styles/**/*.{scss,css}'], ['styles', reload]);
  gulp.watch(['app/scripts/**/*.js'], ['jshint']);
  gulp.watch(['app/images/**/*'], reload);
});

// Build and serve the output from the dist build
gulp.task('serve:dist', ['default'], function () {
  browserSync({
    notify: false,
    // Run as an https by uncommenting 'https: true'
    // Note: this uses an unsigned certificate which on first access
    //       will present a certificate warning in the browser.
    // https: true,
    server: 'dist'
  });
});

// Build Production Files, the Default Task
gulp.task('default', ['clean'], function (cb) {
  runSequence('styles',
              ['jshint', 'html', 'images', 'fonts', 'copy'],
              'rev',
              'rev-replace',
              cb);
});

// Run PageSpeed Insights
// Update `url` below to the public URL for your site
gulp.task('pagespeed', pagespeed.bind(null, {
  // By default, we use the PageSpeed Insights
  // free (no API key) tier. You can use a Google
  // Developer API key if you have one. See
  // http://goo.gl/RkN0vE for info key: 'YOUR_API_KEY'
  url: 'https://www.sergiorus.com',
  strategy: 'mobile'
}));

// Load custom tasks from the `tasks` directory
try { require('require-dir')('tasks'); } catch (err) {}
<commit_msg>Avoid minifying HTML<commit_after>/**
 *
 *  Web Starter Kit
 *  Copyright 2014 Google Inc. All rights reserved.
 *
 *  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
 *
 */

'use strict';

// Include Gulp & Tools We'll Use
var gulp = require('gulp');
var $ = require('gulp-load-plugins')();
var del = require('del');
var runSequence = require('run-sequence');
var browserSync = require('browser-sync');
var pagespeed = require('psi');
var reload = browserSync.reload;

var AUTOPREFIXER_BROWSERS = [
  'ie >= 10',
  'ie_mob >= 10',
  'ff >= 30',
  'chrome >= 34',
  'safari >= 7',
  'opera >= 23',
  'ios >= 7',
  'android >= 4.4',
  'bb >= 10'
];

// Lint JavaScript
gulp.task('jshint', function () {
  return gulp.src('app/scripts/**/*.js')
    .pipe(reload({stream: true, once: true}))
    .pipe($.jshint())
    .pipe($.jshint.reporter('jshint-stylish'))
    .pipe($.if(!browserSync.active, $.jshint.reporter('fail')));
});

// Optimize Images
gulp.task('images', function () {
  return gulp.src('app/images/**/*')
    .pipe($.cache($.imagemin({
      progressive: true,
      interlaced: true
    })))
    .pipe(gulp.dest('dist/images'))
    .pipe($.size({title: 'images'}));
});

// Copy All Files At The Root Level (app)
gulp.task('copy', function () {
  return gulp.src([
    'app/*',
    '!app/*.html',

  ], {
    dot: true
  }).pipe(gulp.dest('dist'))
    .pipe($.size({title: 'copy'}));
});

// Copy Web Fonts To Dist
gulp.task('fonts', function () {
  return gulp.src(['app/fonts/**'])
    .pipe(gulp.dest('dist/fonts'))
    .pipe($.size({title: 'fonts'}));
});

// Compile and Automatically Prefix Stylesheets
gulp.task('styles', function () {
  // For best performance, don't add Sass partials to `gulp.src`
  return gulp.src([
    'app/styles/*.scss',
    'app/styles/**/*.css',
    'app/styles/components/components.scss'
  ])
    .pipe($.changed('styles', {extension: '.scss'}))
    .pipe($.rubySass({
      style: 'expanded',
      precision: 10
    }))
    .on('error', console.error.bind(console))
    .pipe($.autoprefixer({browsers: AUTOPREFIXER_BROWSERS}))
    .pipe(gulp.dest('.tmp/styles'))
    // Concatenate And Minify Styles
    .pipe($.if('*.css', $.csso()))
    .pipe(gulp.dest('dist/styles'))
    .pipe($.size({title: 'styles'}));
});

// Scan Your HTML For Assets & Optimize Them
gulp.task('html', function () {
  var assets = $.useref.assets({searchPath: '{.tmp,app}'});

  return gulp.src('app/_includes/*.html')
    .pipe(assets)
    // Concatenate And Minify JavaScript
    .pipe($.if('*.js', $.uglify({preserveComments: 'some'})))
    // Remove Any Unused CSS
    // Note: If not using the Style Guide, you can delete it from
    // the next line to only include styles your project uses.
    // .pipe($.if('*.css', $.uncss({
    //   html: [
    //     'app/index.html'
    //     // 'app/styleguide.html'
    //   ],
    //   // CSS Selectors for UnCSS to ignore
    //   // ignore: [
    //   //   /.side-nav.open/,
    //   //   /.header-bar.open/
    //   // ]
    // })))
    // Concatenate And Minify Styles
    // In case you are still using useref build blocks
    .pipe($.if('*.css', $.csso()))
    .pipe(assets.restore())
    .pipe($.useref())
    // Update Production Style Guide Paths
    .pipe($.replace('components/components.css', 'components/main.min.css'))
    // Minify Any HTML
    // .pipe($.if('*.html', $.minifyHtml()))
    // Output Files
    .pipe(gulp.dest('dist'))
    .pipe($.size({title: 'html'}));
});

// Static assets revisioning: unicorn.css → unicorn-098f6bcd.css
gulp.task('rev', function () {
  return gulp.src(['dist/styles/*.css', 'dist/scripts/*.js'], {base: 'dist'})
    .pipe(gulp.dest('dist'))
    .pipe($.rev())
    .pipe(gulp.dest('dist'))
    .pipe($.rev.manifest('dist/rev-manifest.json', {base: 'dist'}))
    .pipe(gulp.dest('dist'));
});

// Rewrite occurences of filenames which have been renamed by 'rev' task
gulp.task('rev-replace', function () {
  var manifest = gulp.src('dist/rev-manifest.json');
  return gulp.src('dist/*.html')
    .pipe($.revReplace({manifest: manifest}))
    .pipe(gulp.dest('dist'));
});

// Clean Output Directory
gulp.task('clean', del.bind(null, ['.tmp', 'dist/*', '!dist/.git']));

// Watch Files For Changes & Reload
gulp.task('serve', ['styles'], function () {
  browserSync({
    notify: false,
    // Run as an https by uncommenting 'https: true'
    // Note: this uses an unsigned certificate which on first access
    //       will present a certificate warning in the browser.
    // https: true,
    server: ['.tmp', 'app']
  });

  gulp.watch(['app/**/*.html'], reload);
  gulp.watch(['app/styles/**/*.{scss,css}'], ['styles', reload]);
  gulp.watch(['app/scripts/**/*.js'], ['jshint']);
  gulp.watch(['app/images/**/*'], reload);
});

// Build and serve the output from the dist build
gulp.task('serve:dist', ['default'], function () {
  browserSync({
    notify: false,
    // Run as an https by uncommenting 'https: true'
    // Note: this uses an unsigned certificate which on first access
    //       will present a certificate warning in the browser.
    // https: true,
    server: 'dist'
  });
});

// Build Production Files, the Default Task
gulp.task('default', ['clean'], function (cb) {
  runSequence('styles',
              ['jshint', 'html', 'images', 'fonts', 'copy'],
              'rev',
              'rev-replace',
              cb);
});

// Run PageSpeed Insights
// Update `url` below to the public URL for your site
gulp.task('pagespeed', pagespeed.bind(null, {
  // By default, we use the PageSpeed Insights
  // free (no API key) tier. You can use a Google
  // Developer API key if you have one. See
  // http://goo.gl/RkN0vE for info key: 'YOUR_API_KEY'
  url: 'https://www.sergiorus.com',
  strategy: 'mobile'
}));

// Load custom tasks from the `tasks` directory
try { require('require-dir')('tasks'); } catch (err) {}
<|endoftext|>


<commit_before>import GameScene from '../GameScene/GameScene';

export default class BaseStrategy {
    constructor(path = null) {
        if (path) {
            const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
            this._socket = new WebSocket(`${protocol}//${path}`);
        }

        this.stage = null;
        this.stages = [];
        this.scene = new GameScene();
        this.scene.setStage(null);
    }

    next() {
        if (this.stages.length === 0) {
            this.finish();
            return;
        }

        this.stage = this.stages.shift();
        this.scene.setStage(this.stage);
    }

    pause() {
        if (!this.isFinished()) {
            this.scene.pause();
        }
    }

    resume() {
        this.scene.resume();
    }

    isFinished() {
        return !this.scene;
    }

    finish() {
        this.scene.destroy();
        delete this.scene;
        this._socket.close();
    }
}
<commit_msg>Fix socket closing<commit_after>import GameScene from '../GameScene/GameScene';

export default class BaseStrategy {
    constructor(path = null) {
        if (path) {
            const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
            this._socket = new WebSocket(`${protocol}//${path}`);
        }

        this.stage = null;
        this.stages = [];
        this.scene = new GameScene();
        this.scene.setStage(null);
    }

    next() {
        if (this.stages.length === 0) {
            this.finish();
            return;
        }

        this.stage = this.stages.shift();
        this.scene.setStage(this.stage);
    }

    pause() {
        if (!this.isFinished()) {
            this.scene.pause();
        }
    }

    resume() {
        this.scene.resume();
    }

    isFinished() {
        return !this.scene;
    }

    finish() {
        this.scene.destroy();
        delete this.scene;
        if (this._socket) {
            this._socket.close();
        }
    }
}
<|endoftext|>