<commit_before>// Copyright 2016-2021, University of Colorado Boulder

/**
 * Shows a Dialog with content describing keyboard interactions. Brought up by a button
 * in the navigation bar.
 *
 * @author Jesse Greenberg
 */

import merge from '../../phet-core/js/merge.js';
import KeyboardHelpSection from '../../scenery-phet/js/keyboard/help/KeyboardHelpSection.js';
import TextKeyNode from '../../scenery-phet/js/keyboard/TextKeyNode.js';
import PhetFont from '../../scenery-phet/js/PhetFont.js';
import { HBox, Node, PDOMPeer, ReadingBlock, VBox, VoicingText } from '../../scenery/js/imports.js';
import Dialog from '../../sun/js/Dialog.js';
import Tandem from '../../tandem/js/Tandem.js';
import joist from './joist.js';
import joistStrings from './joistStrings.js';

// constants
const TITLE_MAX_WIDTH = 670;

const tabToGetStartedString = joistStrings.a11y.keyboardHelp.tabToGetStarted;

class KeyboardHelpDialog extends Dialog {

  /**
   * @param {Node} helpContent - a node containing the sim specific keyboard help content
   * @param {Object} [options]
   */
  constructor( helpContent, options ) {

    options = merge( {
      titleAlign: 'center',
      fill: 'rgb( 214, 237, 249 )',
      ySpacing: 15,
      tandem: Tandem.REQUIRED,

      phetioReadOnly: true, // the KeyboardHelpDialog should not be settable
      phetioDynamicElement: true,

      // Because of the special titleNode, we set the aria-labelledby attribute manually; see below.
      addAriaLabelledByFromTitle: false
    }, options );

    // title
    assert && assert( !options.title, 'KeyboardHelpDialog sets title' );
    const shortcutsTitleText = new VoicingText( joistStrings.keyboardShortcuts.title, {
      font: new PhetFont( {
        weight: 'bold',
        size: 24
      } ),
      maxWidth: TITLE_MAX_WIDTH,

      // pdom options
      tagName: 'h1',
      innerContent: joistStrings.a11y.keyboardHelp.keyboardShortcuts
    } );

    // stack the two items with a bit of spacing
    options.title = new VBox( {
        children: [
          shortcutsTitleText,
          new TabHintLine()
        ],
        spacing: 5,

        // pdom
        tagName: 'div'
      }
    );

    // help content surrounded by a div unless already specified, so that all content is read when dialog opens
    helpContent.tagName = helpContent.tagName || 'div';

    super( helpContent, options );

    // (a11y) Make sure that the title passed to the Dialog has an accessible name.
    this.addAriaLabelledbyAssociation( {
      thisElementName: PDOMPeer.PRIMARY_SIBLING,
      otherNode: shortcutsTitleText,
      otherElementName: PDOMPeer.PRIMARY_SIBLING
    } );
  }
}

/**
 * An inner class that assembles the "Tab to get started" content of the Dialog title. This content
 * is interactive with Voicing in that it can be clicked to hear this content (when Voicing is enabled).
 */
class TabHintLine extends Node {

  /**
   * @mixes ReadingBlock
   * @param {Object} [options]
   */
  constructor( options ) {

    options = merge( {
      readingBlockContent: tabToGetStartedString
    }, options );

    super();

    // a line to say "tab to get started" below the "Keyboard Shortcuts" 'title'
    const labelWithIcon = KeyboardHelpSection.labelWithIcon( joistStrings.keyboardShortcuts.toGetStarted,
      TextKeyNode.tab(), tabToGetStartedString, {
        iconOptions: {
          tagName: 'p' // because there is only one, and the default is an li tag
        }
      } );

    // labelWithIcon is meant to be passed to KeyboardHelpSection, so we have to hack a bit here
    this.addChild( new HBox( { children: [ labelWithIcon.icon, labelWithIcon.label ], spacing: 4 } ) );

    this.initializeReadingBlock();
    this.mutate( options );
  }
}

ReadingBlock.compose( TabHintLine );

joist.register( 'KeyboardHelpDialog', KeyboardHelpDialog );
export default KeyboardHelpDialog;<commit_msg>labelInnerContent should be optional, https://github.com/phetsims/scenery-phet/issues/717<commit_after>// Copyright 2016-2021, University of Colorado Boulder

/**
 * Shows a Dialog with content describing keyboard interactions. Brought up by a button
 * in the navigation bar.
 *
 * @author Jesse Greenberg
 */

import merge from '../../phet-core/js/merge.js';
import KeyboardHelpSection from '../../scenery-phet/js/keyboard/help/KeyboardHelpSection.js';
import TextKeyNode from '../../scenery-phet/js/keyboard/TextKeyNode.js';
import PhetFont from '../../scenery-phet/js/PhetFont.js';
import { HBox, Node, PDOMPeer, ReadingBlock, VBox, VoicingText } from '../../scenery/js/imports.js';
import Dialog from '../../sun/js/Dialog.js';
import Tandem from '../../tandem/js/Tandem.js';
import joist from './joist.js';
import joistStrings from './joistStrings.js';

// constants
const TITLE_MAX_WIDTH = 670;

const tabToGetStartedString = joistStrings.a11y.keyboardHelp.tabToGetStarted;

class KeyboardHelpDialog extends Dialog {

  /**
   * @param {Node} helpContent - a node containing the sim specific keyboard help content
   * @param {Object} [options]
   */
  constructor( helpContent, options ) {

    options = merge( {
      titleAlign: 'center',
      fill: 'rgb( 214, 237, 249 )',
      ySpacing: 15,
      tandem: Tandem.REQUIRED,

      phetioReadOnly: true, // the KeyboardHelpDialog should not be settable
      phetioDynamicElement: true,

      // Because of the special titleNode, we set the aria-labelledby attribute manually; see below.
      addAriaLabelledByFromTitle: false
    }, options );

    // title
    assert && assert( !options.title, 'KeyboardHelpDialog sets title' );
    const shortcutsTitleText = new VoicingText( joistStrings.keyboardShortcuts.title, {
      font: new PhetFont( {
        weight: 'bold',
        size: 24
      } ),
      maxWidth: TITLE_MAX_WIDTH,

      // pdom options
      tagName: 'h1',
      innerContent: joistStrings.a11y.keyboardHelp.keyboardShortcuts
    } );

    // stack the two items with a bit of spacing
    options.title = new VBox( {
        children: [
          shortcutsTitleText,
          new TabHintLine()
        ],
        spacing: 5,

        // pdom
        tagName: 'div'
      }
    );

    // help content surrounded by a div unless already specified, so that all content is read when dialog opens
    helpContent.tagName = helpContent.tagName || 'div';

    super( helpContent, options );

    // (a11y) Make sure that the title passed to the Dialog has an accessible name.
    this.addAriaLabelledbyAssociation( {
      thisElementName: PDOMPeer.PRIMARY_SIBLING,
      otherNode: shortcutsTitleText,
      otherElementName: PDOMPeer.PRIMARY_SIBLING
    } );
  }
}

/**
 * An inner class that assembles the "Tab to get started" content of the Dialog title. This content
 * is interactive with Voicing in that it can be clicked to hear this content (when Voicing is enabled).
 */
class TabHintLine extends Node {

  /**
   * @mixes ReadingBlock
   * @param {Object} [options]
   */
  constructor( options ) {

    options = merge( {
      readingBlockContent: tabToGetStartedString
    }, options );

    super();

    // a line to say "tab to get started" below the "Keyboard Shortcuts" 'title'
    const labelWithIcon = KeyboardHelpSection.labelWithIcon( joistStrings.keyboardShortcuts.toGetStarted,
      TextKeyNode.tab(), {
        labelInnerContent: tabToGetStartedString,
        iconOptions: {
          tagName: 'p' // because there is only one, and the default is an li tag
        }
      } );

    // labelWithIcon is meant to be passed to KeyboardHelpSection, so we have to hack a bit here
    this.addChild( new HBox( { children: [ labelWithIcon.icon, labelWithIcon.label ], spacing: 4 } ) );

    this.initializeReadingBlock();
    this.mutate( options );
  }
}

ReadingBlock.compose( TabHintLine );

joist.register( 'KeyboardHelpDialog', KeyboardHelpDialog );
export default KeyboardHelpDialog;<|endoftext|>


<commit_before>/*-------------------------------------------------------+
| SYSTOPIA Contact Segmentation Extension                |
| Copyright (C) 2017 SYSTOPIA                            |
| Author: B. Endres (endres@systopia.de)                 |
| http://www.systopia.de/                                |
+-------------------------------------------------------*/

var segments_details    = SEGMENTS_DETAILS;
var segments_field_id   = "SEGMENTS_FIELD_ID";
var membership_details  = MEMBERSHIP_DETAILS;
var membership_field_id = "MEMBERSHIP_FIELD_ID";
var campaign_details    = CAMPAIGN_DETAILS;
var campaign_status     = CAMPAIGN_STATUS;
var campaign_field_id   = "CAMPAIGN_FIELD_ID";
var table_wrapper = cj("#custom-SEGMENT_GROUP_ID-table-wrapper");
var panel = table_wrapper.parent();

// remove the "Add Segments Record" button
panel.find("a.button[accesskey='N']")
     .hide();

// blank the 6th column (actions) completely
panel.find("td:nth-child(6)").html('');

// replace segment / campaign / membership labels
panel.find("td[class^='crmf-custom_" + segments_field_id + "']").each(function() {
  cj(this).html(segments_details[cj(this).html()]);
});

panel.find("td[class^='crmf-custom_" + campaign_field_id + "']").each(function() {
  if (campaign_status[cj(this).html()] == '1') {
    cj(this).closest("tr").addClass("segmentation-planned");
  }
  cj(this).html(campaign_details[cj(this).html()]);
});

panel.find("td[class^='crmf-custom_" + membership_field_id + "']").each(function() {
  cj(this).html(membership_details[cj(this).html()]);
});

// sort table by first column (date)
var dt_hooked = false;
cj(table_wrapper).bind("DOMSubtreeModified", function(){
  if (dt_hooked) return;
  var dt = table_wrapper.find("table.dataTable");
  if (dt.length) {
    // there, the datatable is finally here -> add event hook
    cj(dt).on("init.dt", function(e, myDT) {
      table_wrapper.find("table.dataTable").dataTable().fnSort([[0,'desc']]);
    });
    dt_hooked = true;
  }
});
<commit_msg>Fix segmentation contact tab not rendering labels<commit_after>/*-------------------------------------------------------+
| SYSTOPIA Contact Segmentation Extension                |
| Copyright (C) 2017 SYSTOPIA                            |
| Author: B. Endres (endres@systopia.de)                 |
| http://www.systopia.de/                                |
+-------------------------------------------------------*/

var segments_details    = SEGMENTS_DETAILS;
var segments_field_id   = "SEGMENTS_FIELD_ID";
var membership_details  = MEMBERSHIP_DETAILS;
var membership_field_id = "MEMBERSHIP_FIELD_ID";
var campaign_details    = CAMPAIGN_DETAILS;
var campaign_status     = CAMPAIGN_STATUS;
var campaign_field_id   = "CAMPAIGN_FIELD_ID";
var table_wrapper = cj("#custom-SEGMENT_GROUP_ID-table-wrapper");
var panel = table_wrapper.parent();

// remove the "Add Segments Record" button
panel.find(".action-link")
     .hide();

var observer = new MutationObserver(function() {
  // blank the 6th column (actions) completely
  panel.find("td:nth-child(6)").html('');

  // replace segment / campaign / membership labels
  panel.find("td[class^='crmf-custom_" + segments_field_id + "']")
    .each(function () {
      cj(this).html(segments_details[cj(this).html()]);
    });

  panel.find("td[class^='crmf-custom_" + campaign_field_id + "']")
    .each(function () {
      if (campaign_status[cj(this).html()] == '1') {
        cj(this).closest("tr").addClass("segmentation-planned");
      }
      cj(this).html(campaign_details[cj(this).html()]);
    });

  panel.find("td[class^='crmf-custom_" + membership_field_id + "']")
    .each(function () {
      cj(this).html(membership_details[cj(this).html()]);
    });
});

observer.observe(table_wrapper.find('table')[0], { childList: true, subtree: true });

// sort table by first column (date)
var dt_hooked = false;
cj(table_wrapper).bind("DOMSubtreeModified", function(){
  if (dt_hooked) return;
  var dt = table_wrapper.find("table.dataTable");
  if (dt.length) {
    // there, the datatable is finally here -> add event hook
    cj(dt).on("init.dt", function(e, myDT) {
      table_wrapper.find("table.dataTable").dataTable().fnSort([[0,'desc']]);
    });
    dt_hooked = true;
  }
});
<|endoftext|>


<commit_before>/*global define*/
define('app/toolbox-opened', ['moment', 'lodash', 'TelemetryPromises', 'DevToolsMetrics', 'LatestVersions', 'FIREFOX_RELEASES'],
function(moment, _, T, DevToolsMetrics, LatestVersions, FIREFOX_RELEASES) {

  var metric = 'DEVTOOLS_TOOLBOX_OPENED_BOOLEAN';
  var newMetric = 'DEVTOOLS_TOOLBOX_OPENED_COUNT';
  // the old BOOLEAN metric is being phased out so this dictionary
  // (assuming it is updated per release) will help us show activity as the
  // new metric moves in and the old metric fades away.
  var metrics = {
    'nightly': newMetric,
    'aurora': newMetric,
    'beta': metric,
    'release': metric
  };

  var options = { sanitized: true };

  var ID = 'devtools-toolbox-opened-chart';
  var chart = {
    title: 'Toolbox Opened Per User / Day (DAU)',
    description: 'Telemetry numbers report how many times users have opened the DevTools toolbox in the day.',
    width: 335,
    height: 320,
    left: 60,
    legend: T.ALL_CHANNELS,
    colors: _.values(FIREFOX_RELEASES.colors),
    legend_target: '#' + ID + '-legend'
  };

  function evolutionMap(channel, evolutions) {
    // map the data into the values we need
    // histogram, index, date
    return evolutions.map((h, i, date) => {
      return {
        channel: channel,
        value: h.sum,
        date: date
      };
    });
  }

  DevToolsMetrics.line(ID, chart);

  return LatestVersions.getLatestVersion().then((versions) => {
            var total = versions.reduce((prev, curr) => (prev + curr.versions.length), 0);
            var current = total;
            DevToolsMetrics.progress(ID, total, current);
            return Promise.all(versions.map((target) => {
              return Promise.all(target.versions.map((version) => {
                return T.getEvolution(target.channel, version, metrics[target.channel], options).then(function (evo) {
                  DevToolsMetrics.progress(ID, total, current -= 1);
                  return evo;
                }).catch(function () {
                  DevToolsMetrics.progress(ID, total, current -= 1);
                  return null;
                });
              })).then((evolutions) => T.reduceEvolutions(evolutions)).
                  then((evolutions) => evolutionMap(target.channel, evolutions)).
                  then(_.flatten);
            })).then((data) => {
              chart.data = data;
              DevToolsMetrics.line(ID, chart);
              return data;
            });
          });

}); // end define
<commit_msg>add marker for new metric going into effect<commit_after>/*global define*/
define('app/toolbox-opened', ['lodash', 'TelemetryPromises', 'DevToolsMetrics', 'LatestVersions', 'FIREFOX_RELEASES'],
function(_, T, DevToolsMetrics, LatestVersions, FIREFOX_RELEASES) {

  var metric = 'DEVTOOLS_TOOLBOX_OPENED_BOOLEAN';
  var newMetric = 'DEVTOOLS_TOOLBOX_OPENED_COUNT';
  // the old BOOLEAN metric is being phased out so this dictionary
  // (assuming it is updated per release) will help us show activity as the
  // new metric moves in and the old metric fades away.
  var metrics = {
    'nightly': newMetric,
    'aurora': newMetric,
    'beta': metric,
    'release': metric
  };

  var options = { sanitized: true };
  var markers = FIREFOX_RELEASES.beta;
  markers.push({
    date: new Date('2016-02-25'),
    label: 'New Count Metric'
  });

  var ID = 'devtools-toolbox-opened-chart';
  var chart = {
    title: 'Toolbox Opened Per User / Day (DAU)',
    description: 'Telemetry numbers report how many times users have opened the DevTools toolbox in the day.',
    width: 335,
    height: 320,
    left: 60,
    legend: T.ALL_CHANNELS,
    colors: _.values(FIREFOX_RELEASES.colors),
    legend_target: '#' + ID + '-legend',
    markers: markers
  };

  function evolutionMap(channel, evolutions) {
    // map the data into the values we need
    // histogram, index, date
    return evolutions.map((h, i, date) => {
      return {
        channel: channel,
        value: h.sum,
        date: date
      };
    });
  }

  DevToolsMetrics.line(ID, chart);

  return LatestVersions.getLatestVersion().then((versions) => {
            var total = versions.reduce((prev, curr) => (prev + curr.versions.length), 0);
            var current = total;
            DevToolsMetrics.progress(ID, total, current);
            return Promise.all(versions.map((target) => {
              return Promise.all(target.versions.map((version) => {
                return T.getEvolution(target.channel, version, metrics[target.channel], options).then(function (evo) {
                  DevToolsMetrics.progress(ID, total, current -= 1);
                  return evo;
                }).catch(function () {
                  DevToolsMetrics.progress(ID, total, current -= 1);
                  return null;
                });
              })).then((evolutions) => T.reduceEvolutions(evolutions)).
                  then((evolutions) => evolutionMap(target.channel, evolutions)).
                  then(_.flatten);
            })).then((data) => {
              chart.data = data;
              DevToolsMetrics.line(ID, chart);
              return data;
            });
          });

}); // end define
<|endoftext|>


<commit_before>'use strict';

const WsClient = require ('./WsClient')

module.exports = class SignalRClient extends WsClient {

    // todo: implement signalr

}

/*

// signalr-client
//By: [Matthew Whited](mailto:matt@whited.us?subject=signalr-client)  (c) 2018
// v0.0.17

//modifications made to add support for https/wss By: Anthony DiPierro

//TODO: Fix HTTPS Proxy
// https://newspaint.wordpress.com/2012/11/05/node-js-http-and-https-proxy/
// ~Vincent Miceli
//TODO: consider changing binding/start order

var url = require('url'),
    querystring = require('querystring'),
    http = require('http'),
    https = require('https'),
    WebSocketClient = require('websocket').client; // https://github.com/Worlize/WebSocket-Node

var states = {
    connection: {
        unbound: 0,
        bound: 1,
        connecting: 2,
        connected: 3,
        disconnecting: 4,
        disconnected: 5,
        connectFailed: 6,
        errorOccured: 7,
        retryingConnection: 8,
        bindingError: 9,
        retryingFailed: 10,
    },
    getConnection: function (code) {
        for (value in states.connection) {
            if (states.connection[value] == code)
                return value;
        }        ;
        return undefined;
    }
};

function toCleanHubNames(hubNames) {
    var res = [], o = 0;

    if (hubNames.length && hubNames.length > 0) {
        for (var i = 0; i < hubNames.length; i++) {
            var p = hubNames[i];
            if (typeof p === "string") {
                res[o++] = { name: p.toLowerCase() };
            }
        }
    }

    return res;
}

function removeUndefinedProperties(obj) {
    if (obj) {
        for (var propName in obj) {
            if (typeof obj[propName] == "undefined") {
                delete obj[propName];
            }
        }
    }
}
function mergeFrom(target, source) {
    target = target || {};

    if (source) {
        for (var propName in source) {
            target[propName] = source[propName];
        }
    }
    else {
        target = {};
    }
}

function negotiateProxies(baseUrl, hubNames, onSuccess, onError, _client) {

    var cleanedHubs = toCleanHubNames(hubNames);
    if (!cleanedHubs || cleanedHubs.length < 1) {
        onError('you must define at least one hub name and they must be typeof string');
        return;
    }

    var negotiateData = "";
    var negotiateUrl = baseUrl + "/negotiate?" + querystring.stringify({
        connectionData: JSON.stringify(cleanedHubs),
        clientProtocol: 1.5
    });
    var negotiateUrlOptions = url.parse(negotiateUrl, true);

    var negotiateFunction = function (res) {
        res.on('data', function (chunk) {
            negotiateData += chunk;
        });
        res.on('end', function (endRes) {
            try {
                if (res.statusCode == 200) {
                    var negotiateObj = JSON.parse(negotiateData);
                    negotiateObj.Hubs = cleanedHubs;
                    onSuccess(negotiateObj);
                } else if (res.statusCode == 401 || res.statusCode == 302) {
                    if (_client.serviceHandlers.onUnauthorized) {
                        _client.serviceHandlers.onUnauthorized(res);
                    } else {
                        onError('Negotiate Unauthorized', undefined, res.statusCode);
                    }
                } else {
                    onError('Negotiate Unknown', undefined, res.statusCode);
                }
            } catch (e) {
                onError('Parse Error', e, negotiateData);
            }
        });
        res.on('error', function (e) {
            _client.connection.state = states.connection.bindingError;
            if (_client.serviceHandlers.bindingError) {
                _client.serviceHandlers.bindingError(e);
            } else {
                onError('HTTP Error', e);
            }
        });
    };
    var negotiateErrorFunction = function (e) {
        _client.connection.state = states.connection.bindingError;
        if (_client.serviceHandlers.bindingError) {
            _client.serviceHandlers.bindingError(e);
        } else {
            onError('HTTP Negotiate Error', e);
        }
    };

    if (negotiateUrlOptions.headers === undefined) {
        negotiateUrlOptions.headers = {};
    }
    if (_client.headers) {
        for (var propName in _client.headers) {
            negotiateUrlOptions.headers[propName] = _client.headers[propName];
        }
    }

    if (_client.proxy && _client.proxy.host && _client.proxy.port) {
        negotiateUrlOptions.path = negotiateUrlOptions.protocol + '//' + negotiateUrlOptions.host + negotiateUrlOptions.path;
        negotiateUrlOptions.headers.host = negotiateUrlOptions.host;
        negotiateUrlOptions.host = _client.proxy.host;
        negotiateUrlOptions.port = _client.proxy.port;
    }

    if (negotiateUrlOptions.protocol === 'http:') {
        var negotiateResult = http.get(negotiateUrlOptions, negotiateFunction).on('error', negotiateErrorFunction);
    } else if (negotiateUrlOptions.protocol === 'wss:') {
        negotiateUrlOptions.protocol = 'https:';
        var negotiateResult = https.get(negotiateUrlOptions, negotiateFunction).on('error', negotiateErrorFunction);
    } else {
        onError('Protocol Error', undefined, negotiateUrlOptions);
    }
}

function getBindings(baseUrl, hubNames, onSuccess, onError, _client) {
    negotiateProxies(baseUrl, hubNames, function (negotiatedOptions) {

        if (!negotiatedOptions.TryWebSockets) {
            onError('This client only supports websockets', undefined, negotiatedOptions);
            return;
        }

        //negotiatedOptions.Url	                    "/signalr"	        String
        //negotiatedOptions.ProtocolVersion	        "1.2"	            String
        //negotiatedOptions.TryWebSockets	        true	            Boolean
        //negotiatedOptions.ConnectionToken	        "..."	            String
        //negotiatedOptions.ConnectionId	        "..."	            String
        //negotiatedOptions.Hubs                    [{name: "..."}]     Array
        //negotiatedOptions.KeepAliveTimeout	    20	                Number
        //negotiatedOptions.DisconnectTimeout	    30	                Number
        //negotiatedOptions.TransportConnectTimeout	5	                Number

        onSuccess({
            url: baseUrl,
            connection: {
                token: negotiatedOptions.ConnectionToken,
                id: negotiatedOptions.ConnectionId
            },
            timeouts: {
                keepAlive: negotiatedOptions.KeepAliveTimeout,
                disconnect: negotiatedOptions.DisconnectTimeout,
                connect: negotiatedOptions.TransportConnectTimeout
            },
            hubs: negotiatedOptions.Hubs
        });

    }, onError, _client);
}

function getConnectQueryString(_client) {
    var connectData = "";
    var qs = {
        clientProtocol: 1.5,
        transport: "webSockets",
        connectionToken: _client.connection.token,
        connectionData: JSON.stringify(_client.hubData),
        tid: 10
    };

    if (_client.queryString) {
        for (var propName in _client.queryString) {
            qs[propName] = _client.queryString[propName];
        }
    }

    var connectQueryString = _client.url + "/connect?" + querystring.stringify(qs);
    return connectQueryString;
}

function getAbortQueryString(_client) {
    var qs = {
        clientProtocol: 1.5,
        transport: "serverSentEvents",
        connectionData: JSON.stringify(_client.hubData),
        connectionToken: _client.connection.token

    };

    if (_client.queryString) {
        for (var propName in _client.queryString) {
            qs[propName] = _client.queryString[propName];
        }
    }

    var abortQueryString = _client.url + "/abort?" + querystring.stringify(qs);

    return abortQueryString;

}

function getStartQueryString(_client) {
    var qs = {
        clientProtocol: 1.5,
        transport: "webSockets",
        connectionData: JSON.stringify(_client.hubData),
        connectionToken: _client.connection.token
    };

    if (_client.queryString) {
        for (var propName in _client.queryString) {
            qs[propName] = _client.queryString[propName];
        }
    }

    var startQueryString = _client.url + "/start?" + querystring.stringify(qs);

    return startQueryString;
}

function getArgValues(params) {
    var res = [];

    if (params.length && params.length > 1) {
        for (var i = 1; i < params.length; i++) {
            var p = params[i];
            if (typeof p === "function" || typeof p === "undefined") {
                p = null;
            }
            res[i - 1] = p;
        }
    }

    return res;
}

function handlerErrors(errorMessage, e, errorData) {
    console.log("Error Message: ", errorMessage);
    console.log("Exception: ", e);
    console.log("Error Data: ", errorData);

    //throw errorMessage;
}

function buildPayload(hubName, methodName, args, messageId) {
    var data = {
        H: hubName,
        M: methodName,
        A: args,
        I: messageId
    };
    var payload = JSON.stringify(data);
    return payload;
};

function clientInterface(baseUrl, hubs, reconnectTimeout, doNotStart) {
    var client = this;
    var _client = {
        proxy: {},
        headers: {},
        queryString: {},
        pub: client,
        url: baseUrl,
        connection: {
            state: states.connection.unbound,
            token: null,
            id: null
        },
        timeouts: {
            keepAlive: 0,
            disconnect: 0,
            connect: 0
        },
        hubs: [],
        hubData: [],
        handlers: {},
        serviceHandlers: {
            bound: undefined,           // void function(){}
            connectFailed: undefined,   // void function(error){}
            connected: undefined,       // void function(connection){}
            connectionLost: undefined,   // void function(error){}
            disconnected: undefined,    // void function(){}
            onerror: undefined,         // void function(error){}
            messageReceived: undefined, // bool function(message){ return true }
            bindingError: undefined,    // function(error) {}
            onUnauthorized: undefined,  // function(res) {}
            reconnected: undefined,     // void function(connection){}
            reconnecting: undefined     // function(retry) { return false; }
        },

        // https://github.com/Worlize/WebSocket-Node
        websocket: {
            client: new WebSocketClient(),
            connection: null,
            messageid: 0,
            reconnectTimeout: reconnectTimeout || 10,
            reconnectCount: 0
        }
    };

    client.__defineGetter__('url', function () { return _client.url; });
    client.__defineGetter__('state', function () {
        var result = {
            code: _client.connection.state,
            desc: states.getConnection(_client.connection.state)
        };
        return result;
    });

    client.__defineGetter__('connection', function () { return _client.connection; });

    client.__defineGetter__('handlers', function () { return _client.handlers; });
    client.__defineSetter__('handlers', function (val) { mergeFrom(_client.handlers, val); });

    client.__defineGetter__('serviceHandlers', function () { return _client.serviceHandlers; });
    client.__defineSetter__('serviceHandlers', function (val) { mergeFrom(_client.serviceHandlers, val); });

    client.__defineGetter__('hubs', function () {
        var ret = [], x = 0;
        for (h in _client.hubs) {
            ret[x++] = h;
        }
        return ret;
    });
    client.__defineGetter__('lastMessageId', function () { return _client.websocket.messageid; });

    client.__defineGetter__('headers', function () {
        removeUndefinedProperties(_client.headers);
        return _client.headers;
    });
    client.__defineSetter__('headers', function (val) { mergeFrom(_client.headers, val); });

    client.__defineGetter__('proxy', function () { return _client.proxy; });
    client.__defineSetter__('proxy', function (val) { mergeFrom(_client.proxy, val); });

    client.__defineGetter__('queryString', function () {
        removeUndefinedProperties(_client.queryString);
        return _client.queryString;
    });
    client.__defineSetter__('queryString', function (val) { mergeFrom(_client.queryString, val); });

    client.hub = function (hubName) {
        _client.start(false);
        return _client.hubs[hubName.toLowerCase()];
    };
    client.on = function (hubName, methodName, callback) {
        var handler = _client.handlers[hubName.toLowerCase()];
        if (!handler) {
            handler = _client.handlers[hubName.toLowerCase()] = {};
        }
        var method = handler[methodName.toLowerCase()] = callback;
    };
    client.end = function () {
        if ((_client.connection.state == states.connection.connecting
            || _client.connection.state == states.connection.connected)
            && _client.websocket.connection) {
            _client.connection.state = states.connection.disconnecting;
            var connection = _client.websocket.connection;
            _client.websocket.connection = undefined;
            connection.close();
        }
    };
    client.invoke = function (hubName, methodName) {
        var hub = client.hub(hubName);
        if (!hub)
            return;
        var args = getArgValues(arguments);
        return hub.invoke.apply(hub, args);
    };

	var callTimeout = 30000;
	var callCallbacks = {};

    client.__defineGetter__('callTimeout', function () { return callTimeout; });
    client.__defineSetter__('callTimeout', function (val) { callTimeout = val; });

    client.call = function (hubName, methodName) {
		var nohub = typeof client.invoke.apply(client, arguments) === 'undefined';
		return {
			done: function (cb, timeout) { // cb(err, result)
				if (nohub) {
					cb('No Hub');
					return;
				}
				var messageId = client.lastMessageId;
				var timeoutId = setTimeout(
					function () {
						delete callCallbacks[messageId];
						cb('Timeout');
					},
					timeout || callTimeout
				);
				callCallbacks[messageId] = function (err, result) {
					clearTimeout(timeoutId);
					delete callCallbacks[messageId];
					cb(err, result);
				};
			}
		};
    };
	function handleCallResult(messageId, err, result) {
		var cb = callCallbacks[messageId];
		if (cb) cb(err, result);
	}

    client.start = function () {
        _client.getBinding();
    };

    _client.invoke = function (_hub, methodName, args) {
        _client.start(false);

        var payload = buildPayload(_hub.data.name, methodName, args, ++_client.websocket.messageid);
        //try to send message to signalR host
        sendPayload(payload);
        return payload;
    };

    function sendPayload(payload) {
        if (_client.websocket.connection) {
            _client.websocket.connection.send(payload);
        } else {
            setImmediate(sendPayload, payload);
        }
    }
    function scheduleReconnection(isInitalRetry) {
        //Ensure state is still reconnecting
        if (_client.connection.state == states.connection.retryingConnection) {
            if (isInitalRetry) {
                _client.websocket.reconnectCount = 0;
            } else {
                _client.websocket.reconnectCount++;
            }
            var cancelRetry = false;
            if (_client.serviceHandlers.reconnecting) {
                var retry = { inital: isInitalRetry, count: _client.websocket.reconnectCount };
                cancelRetry = _client.serviceHandlers.reconnecting.apply(client, [retry]);
            }
            if (!cancelRetry) {
                setTimeout(function () {
                    var connectQueryString = getConnectQueryString(_client);
                    _client.websocket.client.connect(connectQueryString, undefined, undefined, _client.headers);
                }, 1000 * _client.websocket.reconnectTimeout);
                return true;
            }
            else {
                _client.connection.state = states.connection.retryingFailed;
            }
        }
        return false;
    }

    function abort() {

        if (_client.connection.state === states.connection.disconnected || _client.connection.state === states.connection.disconnecting) {

            var abortQueryString = getAbortQueryString(_client);

            var abortUrlOptions = url.parse(abortQueryString, true);
            var requestObject = undefined;

            if (abortUrlOptions.protocol === 'http:') {
                requestObject = http;
            } else if (abortUrlOptions.protocol === 'wss:') {
                abortUrlOptions.protocol = 'https:';
                requestObject = https;
            } else {
                handlerErrors('Protocol Error', undefined, abortUrlOptions);
            }

            abortUrlOptions = {
                hostname: abortUrlOptions.hostname,
                port: abortUrlOptions.port,
                method: 'POST',
                path: abortUrlOptions.path
            };

            var req = requestObject.request(abortUrlOptions,
            function (res) {

                //console.log('STATUS: ' + res.statusCode);
                //console.log('HEADERS: ' + JSON.stringify(res.headers));

                res.on('data', function (chunk) {
                    //str += chunk;
                });

                res.on('end', function () {
                    console.log('Connection aborted');

                });

            });

            req.on('error', function (e) {
                handlerErrors('Can\'t abort connection', e, abortUrlOptions);
            });

            req.end();
        }

    };

    function startCommunication(onSuccess, onError) {

        var startUrl = getStartQueryString(_client);

        var startUrlOptions = url.parse(startUrl, true);

        var startData = "";
        var startFunction = function (res) {
            res.on('data', function (chunk) {
                startData += chunk;
            });
            res.on('end', function (endRes) {
                try {
                    if (res.statusCode == 200) {
                        var startObj = JSON.parse(startData);
                        onSuccess(startObj);
                    } else if (res.statusCode == 401 || res.statusCode == 302) {
                        if (_client.serviceHandlers.onUnauthorized) {
                            _client.serviceHandlers.onUnauthorized(res);
                        } else {
                            console.log('start::Unauthorized (' + res.statusCode + ')');
                        }
                    } else {
                        console.log('start::unknown (' + res.statusCode + ')');
                    }
                } catch (e) {
                    onError('Parse Error', e, startData);
                }
            });
            res.on('error', function (e) {
                _client.connection.state = states.connection.bindingError;
                if (_client.serviceHandlers.bindingError) {
                    _client.serviceHandlers.bindingError(e);
                } else {
                    onError('HTTP Error', e);
                }
            });
        }

        var startErrorFunction = function (e) {
            _client.connection.state = states.connection.bindingError;
            if (_client.serviceHandlers.bindingError) {
                _client.serviceHandlers.bindingError(e);
            } else {
                onError('HTTP start Error', e);
            }
        };

        if (startUrlOptions.headers === undefined) {
            startUrlOptions.headers = {};
        }
        if (_client.headers) {
            for (var propName in _client.headers) {
                startUrlOptions.headers[propName] = _client.headers[propName];
            }
        }

        if (_client.proxy && _client.proxy.host && _client.proxy.port) {
            startUrlOptions.path = startUrlOptions.protocol + '//' + startUrlOptions.host + startUrlOptions.path;
            startUrlOptions.headers.host = startUrlOptions.host;
            startUrlOptions.host = _client.proxy.host;
            startUrlOptions.port = _client.proxy.port;
        }

        if (startUrlOptions.protocol === 'http:') {
            var startResult = http.get(startUrlOptions, startFunction).on('error', startErrorFunction);
        } else if (startUrlOptions.protocol === 'wss:') {
            startUrlOptions.protocol = 'https:';
            var startResult = https.get(startUrlOptions, startFunction).on('error', startErrorFunction);
        } else {
            onError('Protocol Error', undefined, startUrlOptions);
        }

    };

    _client.start = function (tryOnceAgain) {
        //connected: 3,
        //retryingConnection: 8,
        //connecting: 2,
        if (_client.connection.state == states.connection.connected
            || _client.connection.state == states.connection.retryingConnection
            || _client.connection.state == states.connection.connecting) {
            return true;
        }
        //unbound: 0,
        //bindingError: 9,
        else if (_client.connection.state == states.connection.bindingError
            || _client.connection.state == states.connection.unbound) {
            if (!tryOnceAgain) {
                _client.getBinding();
                setImmediate(_client.start, true);
            }
            return false;
        }
        //bound: 1,
        //disconnecting: 4,
        //disconnected: 5,
        //connectFailed: 6,
        //errorOccured: 7,
        else if (_client.connection.state == states.connection.bound
            || _client.connection.state == states.connection.disconnecting
            || _client.connection.state == states.connection.disconnected
            || _client.connection.state == states.connection.connectFailed
            || _client.connection.state == states.connection.errorOccured) {
            _client.connection.state = states.connection.connecting;

            //connect to websockets
            var connectQueryString = getConnectQueryString(_client);
            _client.websocket.client.connect(connectQueryString, undefined, undefined, _client.headers);
            return false;
        }
        return true;
    };

    _client.websocket.client.on('connectFailed', function (error) {
        if (_client.connection.state == states.connection.retryingConnection
            && scheduleReconnection(false)) {
        } else {
            _client.connection.state = states.connection.connectFailed;
            if (_client.serviceHandlers.connectFailed) {
                _client.serviceHandlers.connectFailed.apply(client, [error]);
            } else {
                console.log("Connect Failed!");
            }
        }
    });
    _client.websocket.client.on('connect', function (connection) {
        _client.websocket.connection = connection;
        _client.websocket.messageid = 0; //Reset MessageID on new connection

        //Note: check for reconnecting
        if (_client.connection.state == states.connection.retryingConnection) {
            //Note: reconnected event
            if (_client.serviceHandlers.reconnected) {
                _client.serviceHandlers.reconnected.apply(client, [connection]);
            } else {
                console.log("Reconnected!");
            }
        } else {
            if (_client.serviceHandlers.connected) {
                startCommunication(function (data) {

                    _client.serviceHandlers.connected.apply(client, [connection]);
                },
                handlerErrors);
            } else {
                console.log("Connected!");
            }
        }
        connection.on('error', function (error) {
            _client.websocket.connection = undefined;
            _client.connection.state = states.connection.errorOccured;

            //Note: Add support for automatic retry
            if (error.code == "ECONNRESET") {
                _client.connection.state = states.connection.retryingConnection;
                if (_client.serviceHandlers.connectionLost) {
                    _client.serviceHandlers.connectionLost.apply(client, [error]);
                } else {
                    console.log("Scheduled Reconnection: " + error.toString());
                }
                scheduleReconnection(true);
            } else {
                if (_client.serviceHandlers.onerror) {
                    _client.serviceHandlers.onerror.apply(client, [error]);
                } else {
                    console.log("Connection Error: " + error.toString());
                }
            }
        });
        connection.on('close', function () {
            if (_client.connection.state != states.connection.retryingConnection) {
                _client.connection.state = states.connection.disconnected;
            }
            if (_client.serviceHandlers.disconnected) {
                _client.serviceHandlers.disconnected.apply(client);
            }
            //Abort connection
            abort();

            _client.websocket.connection = undefined; //Release connection on close
        });
        connection.on('message', function (message) {
            var handled = false;
            if (_client.serviceHandlers.messageReceived) {
                handled = _client.serviceHandlers.messageReceived.apply(client, [message]);
            }
            if (!handled) {
                //{"C":"d-8F1AB453-B,0|C,0|D,1|E,0","S":1,"M":[]}
                if (message.type === 'utf8' && message.utf8Data != "{}") {
                    var parsed = JSON.parse(message.utf8Data);

                    //{"C":"d-74C09D5E-B,1|C,0|D,1|E,0","M":[{"H":"TestHub","M":"addMessage","A":["ie","sgds"]}]}
                    if (parsed.hasOwnProperty('M')) {
                        for (var i = 0; i < parsed.M.length; i++) {
                            var mesg = parsed.M[i];
                            var hubName = mesg.H.toLowerCase();
                            var handler = _client.handlers[hubName];
                            if (handler) {
                                var methodName = mesg.M.toLowerCase();
                                var method = handler[methodName];
                                if (method) {
                                    var hub = client.hub(hubName)
                                    method.apply(hub, mesg.A);
                                }
                            }
                        }
                    }
					else if (parsed.hasOwnProperty('I')) {
						handleCallResult(+parsed.I, parsed.E, parsed.R);
					}
                }
            }
        });
    });

    _client.getBinding = function () {
        getBindings(baseUrl, hubs, function (bindings) {

            _client.hubData = bindings.hubs;

            //hubs:
            for (var i = 0; i < bindings.hubs.length; i++) {
                var hubData = bindings.hubs[i];
                _client.hubs[hubData.name] = new hubInterface(_client, hubData);
            }

            //timeouts: { keepAlive: disconnect: connect: },
            _client.timeouts.keepAlive = bindings.timeouts.keepAlive;
            _client.timeouts.disconnect = bindings.timeouts.disconnect;
            _client.timeouts.connect = bindings.timeouts.connect;

            //connection: { token: id: },
            _client.connection.state = states.connection.bound;
            _client.connection.id = bindings.connection.id;
            _client.connection.token = bindings.connection.token;

            if (_client.serviceHandlers.bound) {
                _client.serviceHandlers.bound.apply(client);
            }

            _client.start(true);
        }, function(errorMessage, exception, errorData) {
            if (_client.serviceHandlers.onerror) {
                _client.serviceHandlers.onerror(errorMessage, exception, errorData);
            } else {
                handlerErrors(errorMessage, exception, errorData);
            }
        }, _client);
    };

    if (doNotStart) {
    }
    else {
        _client.getBinding();
    }
}

function hubInterface(_client, hubData) {
    var hub = this;
    var _hub = {
        pub: hub,
        client: _client,
        data: hubData
    };

    hub.__defineGetter__('name', function () { return _hub.data.name; });
    hub.__defineGetter__('client', function () { return _hub.client.pub; });
    hub.__defineGetter__('handlers', function () { return _hub.client.handlers[_hub.data.name]; });
    hub.invoke = function (methodName) {
        var args = getArgValues(arguments);
        return _hub.client.invoke(_hub, methodName, args);
    };
    hub.on = function (methodName, callback) {
        _hub.client.pub.on(_hub.data.name, methodName, callback);
    };
}

module.exports = {
    client: clientInterface
};

*/
<commit_msg>Delete SignalRClient.js<commit_after><|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

const React = require('react')
const ImmutableComponent = require('./immutableComponent')
const Immutable = require('immutable')
const keyCodes = require('../../app/common/constants/keyCodes')
const Button = require('./button')
const SwitchControl = require('../components/switchControl')
const windowActions = require('../actions/windowActions')
const windowStore = require('../stores/windowStore')
const contextMenus = require('../contextMenus')
const {getTextColorForBackground} = require('../lib/color')

class FindBar extends ImmutableComponent {
  constructor () {
    super()
    this.onBlur = this.onBlur.bind(this)
    this.onInputFocus = this.onInputFocus.bind(this)
    this.onClear = this.onClear.bind(this)
    this.onKeyDown = this.onKeyDown.bind(this)
    this.onContextMenu = this.onContextMenu.bind(this)
    this.onKeyUp = this.onKeyUp.bind(this)
    this.onFindPrev = this.onFindPrev.bind(this)
    this.onFindNext = this.onFindNext.bind(this)
    this.onCaseSensitivityChange = this.onCaseSensitivityChange.bind(this)
    this.didFrameChange = true
  }

  get frame () {
    return windowStore.getFrame(this.props.frameKey)
  }

  onKeyUp (e) {
    windowActions.setFindDetail(this.frame, Immutable.fromJS({
      searchString: e.target.value,
      caseSensitivity: this.isCaseSensitive
    }))
  }

  onCaseSensitivityChange (e) {
    windowActions.setFindDetail(this.frame, Immutable.fromJS({
      searchString: this.searchString,
      caseSensitivity: !this.isCaseSensitive
    }))
  }

  onFindFirst () {
    this.props.onFind(this.searchString, this.isCaseSensitive, true, false)
  }

  onFindNext () {
    this.props.onFind(this.searchString, this.isCaseSensitive, true, this.props.findDetail.get('internalFindStatePresent'))
  }

  onFindPrev () {
    this.props.onFind(this.searchString, this.isCaseSensitive, false, this.props.findDetail.get('internalFindStatePresent'))
  }

  onContextMenu (e) {
    // Without this timeout selection is not shown when right clicking in
    // a word so the word replacement is kind of a surprise.  This is because
    // our context menus are modal at the moment.  If we fix that we can
    // remove this timeout.
    setTimeout(() =>
      contextMenus.onFindBarContextMenu(e), 10)
  }

  /**
   * Focus the find in page input and select the text
   */
  focus () {
    this.searchInput.focus()
  }

  select () {
    this.searchInput.select()
  }

  componentDidMount () {
    this.searchInput.value = this.searchString
    this.focus()
    this.select()
    windowActions.setFindbarSelected(false)
  }

  componentWillUpdate (nextProps) {
    if (nextProps.frameKey !== this.props.frameKey) {
      this.searchInput.value = nextProps.findDetail && nextProps.findDetail.get('searchString') || ''
    }
  }

  componentDidUpdate (prevProps) {
    if (this.props.selected && !prevProps.selected) {
      this.focus()
      // Findbar might already be focused, so make sure select happens even if no
      // onFocus event happens.
      this.select()
      windowActions.setFindbarSelected(false)
    }
    if (!this.props.findDetail || !prevProps.findDetail ||
        this.props.findDetail.get('searchString') !== prevProps.findDetail.get('searchString') ||
        this.props.findDetail.get('caseSensitivity') !== prevProps.findDetail.get('caseSensitivity')) {
      // Redo search if details have changed
      this.onFindFirst()
    }
  }

  onKeyDown (e) {
    switch (e.keyCode) {
      case keyCodes.ESC:
        e.preventDefault()
        this.props.onFindHide()
        break
      case keyCodes.ENTER:
        e.preventDefault()
        if (e.shiftKey) {
          this.onFindPrev()
        } else {
          this.onFindNext()
        }
        break
    }
  }

  onInputFocus () {
    this.select()
  }

  onBlur (e) {
    windowActions.setFindbarSelected(this.frame, false)
  }

  onClear () {
    this.searchInput.value = ''
    windowActions.setFindDetail(this.frame, Immutable.fromJS({
      searchString: '',
      caseSensitivity: this.isCaseSensitive
    }))
    this.focus()
  }

  get numberOfMatches () {
    if (!this.props.findDetail || this.props.findDetail.get('numberOfMatches') === undefined) {
      return -1
    }
    return this.props.findDetail.get('numberOfMatches')
  }

  get activeMatchOrdinal () {
    if (!this.props.findDetail || this.props.findDetail.get('activeMatchOrdinal') === undefined) {
      return -1
    }
    return this.props.findDetail.get('activeMatchOrdinal') || -1
  }

  get isCaseSensitive () {
    if (!this.props.findDetail) {
      return false
    }
    return this.props.findDetail.get('caseSensitivity')
  }

  get searchString () {
    if (!this.props.findDetail) {
      return ''
    }
    return this.props.findDetail.get('searchString')
  }

  get backgroundColor () {
    return this.props.paintTabs && (this.props.themeColor || this.props.computedThemeColor)
  }

  get textColor () {
    return getTextColorForBackground(this.backgroundColor)
  }

  render () {
    let findMatchText
    if (this.numberOfMatches !== -1 && this.activeMatchOrdinal !== -1 && this.searchString) {
      const l10nArgs = {
        activeMatchOrdinal: this.activeMatchOrdinal,
        numberOfMatches: this.numberOfMatches
      }
      findMatchText =

    } else if (this.numberOfMatches !== -1 && this.searchString) {
      const l10nArgs = {
        activeMatchOrdinal: this.activeMatchOrdinal,
        numberOfMatches: this.numberOfMatches
      }
      findMatchText =

    }

    const backgroundColor = this.backgroundColor
    let findBarStyle = {}

    if (backgroundColor) {
      findBarStyle = {
        background: backgroundColor,
        color: this.textColor
      }
    }

    return

          <input type='text'
            spellCheck='false'
            onContextMenu={this.onContextMenu}
            ref={(node) => { this.searchInput = node }}
            onFocus={this.onInputFocus}
            onKeyDown={this.onKeyDown}
            onKeyUp={this.onKeyUp} />

        {findMatchText}
        <Button iconClass='findButton fa-caret-up'
          inlineStyles={findBarStyle}
          className='findButton smallButton findPrev'
          disabled={this.numberOfMatches <= 0}
          onClick={this.onFindPrev} />
        <Button iconClass='findButton fa-caret-down'
          inlineStyles={findBarStyle}
          className='findButton smallButton findNext'
          disabled={this.numberOfMatches <= 0}
          onClick={this.onFindNext} />
        <SwitchControl
          id='caseSensitivityCheckbox'
          checkedOn={this.isCaseSensitive}
          onClick={this.onCaseSensitivityChange} />

      +

  }
}

module.exports = FindBar
<commit_msg>Make sure findbar selected state always gets reset<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

const React = require('react')
const ImmutableComponent = require('./immutableComponent')
const Immutable = require('immutable')
const keyCodes = require('../../app/common/constants/keyCodes')
const Button = require('./button')
const SwitchControl = require('../components/switchControl')
const windowActions = require('../actions/windowActions')
const windowStore = require('../stores/windowStore')
const contextMenus = require('../contextMenus')
const {getTextColorForBackground} = require('../lib/color')

class FindBar extends ImmutableComponent {
  constructor () {
    super()
    this.onBlur = this.onBlur.bind(this)
    this.onInputFocus = this.onInputFocus.bind(this)
    this.onClear = this.onClear.bind(this)
    this.onKeyDown = this.onKeyDown.bind(this)
    this.onContextMenu = this.onContextMenu.bind(this)
    this.onKeyUp = this.onKeyUp.bind(this)
    this.onFindPrev = this.onFindPrev.bind(this)
    this.onFindNext = this.onFindNext.bind(this)
    this.onCaseSensitivityChange = this.onCaseSensitivityChange.bind(this)
    this.didFrameChange = true
  }

  get frame () {
    return windowStore.getFrame(this.props.frameKey)
  }

  onKeyUp (e) {
    windowActions.setFindDetail(this.frame, Immutable.fromJS({
      searchString: e.target.value,
      caseSensitivity: this.isCaseSensitive
    }))
  }

  onCaseSensitivityChange (e) {
    windowActions.setFindDetail(this.frame, Immutable.fromJS({
      searchString: this.searchString,
      caseSensitivity: !this.isCaseSensitive
    }))
  }

  onFindFirst () {
    this.props.onFind(this.searchString, this.isCaseSensitive, true, false)
  }

  onFindNext () {
    this.props.onFind(this.searchString, this.isCaseSensitive, true, this.props.findDetail.get('internalFindStatePresent'))
  }

  onFindPrev () {
    this.props.onFind(this.searchString, this.isCaseSensitive, false, this.props.findDetail.get('internalFindStatePresent'))
  }

  onContextMenu (e) {
    // Without this timeout selection is not shown when right clicking in
    // a word so the word replacement is kind of a surprise.  This is because
    // our context menus are modal at the moment.  If we fix that we can
    // remove this timeout.
    setTimeout(() =>
      contextMenus.onFindBarContextMenu(e), 10)
  }

  /**
   * Focus the find in page input and select the text
   */
  focus () {
    this.searchInput.focus()
  }

  select () {
    this.searchInput.select()
  }

  componentDidMount () {
    this.searchInput.value = this.searchString
    this.focus()
    this.select()
    windowActions.setFindbarSelected(this.frame, false)
  }

  componentWillUpdate (nextProps) {
    if (nextProps.frameKey !== this.props.frameKey) {
      this.searchInput.value = nextProps.findDetail && nextProps.findDetail.get('searchString') || ''
    }
  }

  componentDidUpdate (prevProps) {
    if (this.props.selected && !prevProps.selected) {
      this.focus()
      // Findbar might already be focused, so make sure select happens even if no
      // onFocus event happens.
      this.select()
      windowActions.setFindbarSelected(this.frame, false)
    }
    if (!this.props.findDetail || !prevProps.findDetail ||
        this.props.findDetail.get('searchString') !== prevProps.findDetail.get('searchString') ||
        this.props.findDetail.get('caseSensitivity') !== prevProps.findDetail.get('caseSensitivity')) {
      // Redo search if details have changed
      this.onFindFirst()
    }
  }

  onKeyDown (e) {
    switch (e.keyCode) {
      case keyCodes.ESC:
        e.preventDefault()
        this.props.onFindHide()
        break
      case keyCodes.ENTER:
        e.preventDefault()
        if (e.shiftKey) {
          this.onFindPrev()
        } else {
          this.onFindNext()
        }
        break
    }
  }

  onInputFocus () {
    this.select()
  }

  onBlur (e) {
    windowActions.setFindbarSelected(this.frame, false)
  }

  onClear () {
    this.searchInput.value = ''
    windowActions.setFindDetail(this.frame, Immutable.fromJS({
      searchString: '',
      caseSensitivity: this.isCaseSensitive
    }))
    this.focus()
  }

  get numberOfMatches () {
    if (!this.props.findDetail || this.props.findDetail.get('numberOfMatches') === undefined) {
      return -1
    }
    return this.props.findDetail.get('numberOfMatches')
  }

  get activeMatchOrdinal () {
    if (!this.props.findDetail || this.props.findDetail.get('activeMatchOrdinal') === undefined) {
      return -1
    }
    return this.props.findDetail.get('activeMatchOrdinal') || -1
  }

  get isCaseSensitive () {
    if (!this.props.findDetail) {
      return false
    }
    return this.props.findDetail.get('caseSensitivity')
  }

  get searchString () {
    if (!this.props.findDetail) {
      return ''
    }
    return this.props.findDetail.get('searchString')
  }

  get backgroundColor () {
    return this.props.paintTabs && (this.props.themeColor || this.props.computedThemeColor)
  }

  get textColor () {
    return getTextColorForBackground(this.backgroundColor)
  }

  render () {
    let findMatchText
    if (this.numberOfMatches !== -1 && this.activeMatchOrdinal !== -1 && this.searchString) {
      const l10nArgs = {
        activeMatchOrdinal: this.activeMatchOrdinal,
        numberOfMatches: this.numberOfMatches
      }
      findMatchText =

    } else if (this.numberOfMatches !== -1 && this.searchString) {
      const l10nArgs = {
        activeMatchOrdinal: this.activeMatchOrdinal,
        numberOfMatches: this.numberOfMatches
      }
      findMatchText =

    }

    const backgroundColor = this.backgroundColor
    let findBarStyle = {}

    if (backgroundColor) {
      findBarStyle = {
        background: backgroundColor,
        color: this.textColor
      }
    }

    return

          <input type='text'
            spellCheck='false'
            onContextMenu={this.onContextMenu}
            ref={(node) => { this.searchInput = node }}
            onFocus={this.onInputFocus}
            onKeyDown={this.onKeyDown}
            onKeyUp={this.onKeyUp} />

        {findMatchText}
        <Button iconClass='findButton fa-caret-up'
          inlineStyles={findBarStyle}
          className='findButton smallButton findPrev'
          disabled={this.numberOfMatches <= 0}
          onClick={this.onFindPrev} />
        <Button iconClass='findButton fa-caret-down'
          inlineStyles={findBarStyle}
          className='findButton smallButton findNext'
          disabled={this.numberOfMatches <= 0}
          onClick={this.onFindNext} />
        <SwitchControl
          id='caseSensitivityCheckbox'
          checkedOn={this.isCaseSensitive}
          onClick={this.onCaseSensitivityChange} />

      +

  }
}

module.exports = FindBar
<|endoftext|>


<commit_before>/**
 * @fileOverview This file defines the scaled field class
 * @author Jonathan Bronson
 * @exports ScaledField
 */
var Vector = require('../geometry/vector');

module.exports = (function(){

'use strict';

var ScaledField = function(field, scale, bounds) {
  this.field = field;
  this.scale = scale;
  this.bounds = bounds;
};

/**
 * @overide
 */
ScaledField.prototype.valueAt = function(x, y) {
  return this.scale * this.field.valueAt(x,y);
};

/**
 * @overide
 */
ScaledField.prototype.getBounds = function() {
  return this.bounds;
};

/**
 * @overide
 */
ScaledField.prototype.getWidth = function() {
  return this.bounds.width();
};

/**
 * @overide
 */
ScaledField.prototype.getHeight = function() {
  return this.bounds.height();
};

return ScaledField;

}());
<commit_msg>Adding missing jsdoc for scaledfield<commit_after>/**
 * @fileOverview This file defines the scaled field class
 * @author Jonathan Bronson
 * @exports ScaledField
 */
var Vector = require('../geometry/vector');

module.exports = (function(){

'use strict';

/**
* Creates a new ScaledField object
* @class
* @constructor
* @alias ScaledField
* @extends Field
*/
var ScaledField = function(field, scale, bounds) {
  this.field = field;
  this.scale = scale;
  this.bounds = bounds;
};

/**
 * @overide
 */
ScaledField.prototype.valueAt = function(x, y) {
  return this.scale * this.field.valueAt(x,y);
};

/**
 * @overide
 */
ScaledField.prototype.getBounds = function() {
  return this.bounds;
};

/**
 * @overide
 */
ScaledField.prototype.getWidth = function() {
  return this.bounds.width();
};

/**
 * @overide
 */
ScaledField.prototype.getHeight = function() {
  return this.bounds.height();
};

return ScaledField;

}());
<|endoftext|>


<commit_before>"use strict"

const kaomojiCollection = {
    "kaomoji": {
        "angry": [
            "凸(｀0´)凸",
            "凸ಠ益ಠ)凸",
            "凸(⊙▂⊙✖ )",
            "┌П┐(►˛◄’!)",
            "凸(｀⌒´メ)凸",
            "( ︶︿︶)_╭∩╮"
        ],
        "bears": [
            "ʕ •ᴥ•ʔ",
            "ᶘಠᴥಠᶅ"
        ],
        "blushing flower girl": [
            "(˶◕‿◕˶✿)",
            "(⁄ ⁄◕⁄ω⁄◕⁄ ⁄✿)"
        ],
        "cats": [
            "ฅ(＾・ω・＾ฅ)",
            `චᆽච`
        ],
        "christmas": [
            "(＾∇^*)ノ⌒☆ -=★ -=☆*Merry X’mas*☆",
            "。∈・^ミβ(*´ω｀人´ω｀*)*:..｡o○☆*ﾟ¨ﾟMerry Christmas .｡.:*"
        ],
        "confused": [
            "「(°ヘ°)",
            "( ・◇・)？",
            "ヾ(´･ ･｀｡)ノ”",
            "⊙.☉"
        ],
        "dogs": [
            "▼・ᴥ・▼",
            "U・ᴥ・U"
        ],
        "jailed": [
            "||Φ|(|ﾟ|∀|ﾟ|)|Φ||",
            "━━━━||Φ|(|´|Д|`|)|Φ||━━━━"
        ],
        "flower girl": [
            "(　◕‿◕✿)",
            "(◕▽◕✿)"
        ],
        "sad flower girl": [
            "(◕‸ ◕✿) *pout*",
            "(◕︿◕✿)"
        ],
        "table flipping throwing": [
            "(╯°□°）╯︵ ┻━┻",
            "┻━┻ミ＼(≧ﾛ≦＼)"
        ],
        "bears table flipping throwing": [
            "ʕノ•ᴥ•ʔノ ︵ ┻━┻",
            " ┳┳ヾ(T(エ)Tヽ)"
        ],
        "setting the table": [
            "┬──┬ ノ( ゜-゜ノ)",
            "(ヘ･_･)ヘ┳━┳"
        ],
        "good morning": [
            "(*^｡^*)Rise (*^o^*)And ＼(*^0^*)／Shineｰ!!",
            "(_ _)(-.-)(~O~)*yawn*・・(~O~)(-.-)"
        ],
        "kissing kisses": [
            "( ˘ ³˘)♥",
            "(ɔˆ ³(ˆ⌣ˆc)"
        ],
        "throwing kisssing kisses": [
            "★⌒ヽ(●’､＾●)Kiss!",
            "(^з^)-☆Chu!!"
        ],
        "hugs hugging": [
            "(つ・▽・)つ⊂(・▽・⊂)",
            "(つ≧▽≦)つ⊂(・ヮ・⊂)"
        ],
        "happy birthday": [
            "[＼／]…..φ(・д・。) Happy birthday♪",
            "Happy♪ヾ|*ﾟ∀ﾟ*|ﾉ” Birthday♪"
        ],
        "mad": [
            "┗(｀Дﾟ┗(｀ﾟДﾟ´)┛ﾟД´)┛",
            "!!!!|┛*｀Д´|┛・・~~┻━┻　┳━┳"
        ],
        "nervous":[
            "(ﾟωﾟ；)",
            "(*ﾟдﾟ*)ﾄﾞｷﾄﾞｷ",
            "┣¨キ(*ﾟДﾟ*)┣¨キ"
        ],
        "pigs": [
            "(´・(oo)・｀)",
            "(V´・(oo)・｀V)"
        ],
        "deep crying face": [
            "ಥ_ಥ",
            "ಥ╭╮ಥ"
        ],
        "denko shoboon": [
            "(´・ω・｀)",
            "Σ(´・ω・｀)"
        ],
        "raise your dongers": [
            "ヽ༼ຈل͜ຈ༽ﾉ",
            "✺◟༼ຈ ل͜ ຈ༽◞✺"
        ],
        "strolling happy gary": [
            "ᕕ( ᐛ )ᕗ",
            "ᐠ( ᐛ )ᐟ"
        ],
        "it’s here kitaaa": [
            "ｷﾀ━(ﾟ∀ﾟ)━!",
            "ｷﾀ━━━(Дﾟ(○=(ﾟ∀ﾟ)=○)Дﾟ)━━━!!"
        ],
        "laughing": [
            "(´∀｀)",
            "ヽ（´∀｀）ノ"
        ],
        "lenny face": [
            "( ͡° ͜ʖ ͡°)",
            "ᕦ( ͡° ͜ʖ ͡°)ᕤ"
        ],
        "look of disapproval": [
            "ಠ_ಠ",
            "ლ(ಠ益ಠლ)"
        ],
        "love": [
            "(๑♡3♡๑)",
            "(｡♥‿♥｡)"
        ],
        "orz":[
            "_|￣|○",
            "il||li ＿|￣|○ il||l",
            "///orz///",
            "▄█▀█●"
        ],
        "shrug": [
            `¯\\\_(ツ)_/¯`,
            `¯\\\_㋡_/¯`
        ],
        "smiling": [
            "（＾－＾）",
            "（＾O＾）"
        ],
        "smug face": [
            "ಸ‿ಸ",
            "ಇ(ಸ‿ಸ)ಇ"
        ],
        "sparkles": [
            "✧･ﾟ: *✧･ﾟ:* 　　 *:･ﾟ✧*:･ﾟ✧",
            "*＊✿❀　❀✿＊*"
        ],
        "cheerful": [
            "(❁´◡`❁)",
            "ヾ(＠^∇^＠)ノ"
        ],
        "excited": [
            "✧ ─=≡Σ((( つ•̀ω•́)つ",
            "✖‿✖",
            "(((o(*ﾟ▽ﾟ*)o)))"
        ],
        "blushing flattered": [
            "(〃 ω 〃)",
            "‧⁺( ᵒ̴̶̷̥́ ◡ ᵒ̴̶̷̣̥̀ )⁺‧"
        ],
        "sad crying": [
            "o（ｉДｉ）o",
            "˚‧º·(˚ ˃̣̣̥⌓˂̣̣̥ )‧º·˚"
        ],
        "sad depressed": [
            "(◞ ‸ ◟ㆀ)",
            "(╯︵╰)"
        ],
        "seal": [
            "(ᵔᴥᵔ)"
        ],
        "weird awkward": [
            "( ་ ⍸ ་ )",
            "(　☉д⊙)"
        ],
        "weird creepy": [
            "(´⊙◞⊱◟⊙｀)",
            "(´◉◞⊖◟◉｀)"
        ],
        "weird wtf gross": [
            "༼;´༎ຶ ۝ ༎ຶ༽",
            "(◞≼⓪≽◟⋌⋚⋛⋋◞≼⓪≽)"
        ],
        "weird hide stalkerific": [
            "|ω・)",
            "┬┴┬┴┤(･_├┬┴┬┴"
        ]
    },
    "nameMap": {
        // eslint-disable-line
    },
    "nameArr": [
        // eslint-disable-line
    ]
}

const kaomoji = kaomojiCollection.kaomoji

for (const fullName in kaomoji) {
	fullName.split(" ").forEach(partiallyName => {
		while (kaomojiCollection.nameMap[partiallyName]) {
            partiallyName = `_${partiallyName}`
        }
        kaomojiCollection.nameMap[partiallyName] = fullName
        kaomojiCollection.nameArr.push(partiallyName)
	})
}

const nameMap = kaomojiCollection.nameMap
const nameArr = kaomojiCollection.nameArr
<commit_msg>Change kaomoji description words.<commit_after>"use strict"

const kaomojiCollection = {
    "kaomoji": {
        "angry middle finger fuck": [
            "凸(｀0´)凸",
            "凸ಠ益ಠ)凸",
            "凸(⊙▂⊙✖ )",
            "┌П┐(►˛◄’!)",
            "凸(｀⌒´メ)凸",
            "( ︶︿︶)_╭∩╮"
        ],
        "bears": [
            "ʕ •ᴥ•ʔ",
            "ᶘಠᴥಠᶅ"
        ],
        "blushing flower girl": [
            "(˶◕‿◕˶✿)",
            "(⁄ ⁄◕⁄ω⁄◕⁄ ⁄✿)"
        ],
        "cats": [
            "ฅ(＾・ω・＾ฅ)",
            `චᆽච`
        ],
        "christmas xmas": [
            "(＾∇^*)ノ⌒☆ -=★ -=☆*Merry X’mas*☆",
            "。∈・^ミβ(*´ω｀人´ω｀*)*:..｡o○☆*ﾟ¨ﾟMerry Christmas .｡.:*"
        ],
        "confused": [
            "「(°ヘ°)",
            "( ・◇・)？",
            "ヾ(´･ ･｀｡)ノ”",
            "⊙.☉"
        ],
        "dogs": [
            "▼・ᴥ・▼",
            "U・ᴥ・U"
        ],
        "jailed": [
            "||Φ|(|ﾟ|∀|ﾟ|)|Φ||",
            "━━━━||Φ|(|´|Д|`|)|Φ||━━━━"
        ],
        "flower girl": [
            "(　◕‿◕✿)",
            "(◕▽◕✿)"
        ],
        "sad flower girl": [
            "(◕‸ ◕✿) *pout*",
            "(◕︿◕✿)"
        ],
        "table flipping throwing": [
            "(╯°□°）╯︵ ┻━┻",
            "┻━┻ミ＼(≧ﾛ≦＼)"
        ],
        "bears table flipping throwing": [
            "ʕノ•ᴥ•ʔノ ︵ ┻━┻",
            " ┳┳ヾ(T(エ)Tヽ)"
        ],
        "setting the table": [
            "┬──┬ ノ( ゜-゜ノ)",
            "(ヘ･_･)ヘ┳━┳"
        ],
        "good morning": [
            "(*^｡^*)Rise (*^o^*)And ＼(*^0^*)／Shineｰ!!",
            "(_ _)(-.-)(~O~)*yawn*・・(~O~)(-.-)"
        ],
        "kissing kisses": [
            "( ˘ ³˘)♥",
            "(ɔˆ ³(ˆ⌣ˆc)"
        ],
        "throwing kisssing kisses": [
            "★⌒ヽ(●’､＾●)Kiss!",
            "(^з^)-☆Chu!!"
        ],
        "hugs hugging": [
            "(つ・▽・)つ⊂(・▽・⊂)",
            "(つ≧▽≦)つ⊂(・ヮ・⊂)"
        ],
        "happy birthday": [
            "[＼／]…..φ(・д・。) Happy birthday♪",
            "Happy♪ヾ|*ﾟ∀ﾟ*|ﾉ” Birthday♪"
        ],
        "mad": [
            "┗(｀Дﾟ┗(｀ﾟДﾟ´)┛ﾟД´)┛",
            "!!!!|┛*｀Д´|┛・・~~┻━┻　┳━┳"
        ],
        "nervous":[
            "(ﾟωﾟ；)",
            "(*ﾟдﾟ*)ﾄﾞｷﾄﾞｷ",
            "┣¨キ(*ﾟДﾟ*)┣¨キ"
        ],
        "pigs": [
            "(´・(oo)・｀)",
            "(V´・(oo)・｀V)"
        ],
        "deep crying face": [
            "ಥ_ಥ",
            "ಥ╭╮ಥ"
        ],
        "denko shoboon": [
            "(´・ω・｀)",
            "Σ(´・ω・｀)"
        ],
        "raise your dongers": [
            "ヽ༼ຈل͜ຈ༽ﾉ",
            "✺◟༼ຈ ل͜ ຈ༽◞✺"
        ],
        "strolling happy gary": [
            "ᕕ( ᐛ )ᕗ",
            "ᐠ( ᐛ )ᐟ"
        ],
        "it’s here kitaaa": [
            "ｷﾀ━(ﾟ∀ﾟ)━!",
            "ｷﾀ━━━(Дﾟ(○=(ﾟ∀ﾟ)=○)Дﾟ)━━━!!"
        ],
        "laughing": [
            "(´∀｀)",
            "ヽ（´∀｀）ノ"
        ],
        "lenny face": [
            "( ͡° ͜ʖ ͡°)",
            "ᕦ( ͡° ͜ʖ ͡°)ᕤ"
        ],
        "look of disapproval": [
            "ಠ_ಠ",
            "ლ(ಠ益ಠლ)"
        ],
        "love": [
            "(๑♡3♡๑)",
            "(｡♥‿♥｡)"
        ],
        "orz":[
            "_|￣|○",
            "il||li ＿|￣|○ il||l",
            "///orz///",
            "▄█▀█●"
        ],
        "shrug": [
            `¯\\\_(ツ)_/¯`,
            `¯\\\_㋡_/¯`
        ],
        "smiling": [
            "（＾－＾）",
            "（＾O＾）"
        ],
        "smug face": [
            "ಸ‿ಸ",
            "ಇ(ಸ‿ಸ)ಇ"
        ],
        "sparkles": [
            "✧･ﾟ: *✧･ﾟ:* 　　 *:･ﾟ✧*:･ﾟ✧",
            "*＊✿❀　❀✿＊*"
        ],
        "cheerful": [
            "(❁´◡`❁)",
            "ヾ(＠^∇^＠)ノ"
        ],
        "excited": [
            "✧ ─=≡Σ((( つ•̀ω•́)つ",
            "✖‿✖",
            "(((o(*ﾟ▽ﾟ*)o)))"
        ],
        "blushing flattered": [
            "(〃 ω 〃)",
            "‧⁺( ᵒ̴̶̷̥́ ◡ ᵒ̴̶̷̣̥̀ )⁺‧"
        ],
        "sad crying": [
            "o（ｉДｉ）o",
            "˚‧º·(˚ ˃̣̣̥⌓˂̣̣̥ )‧º·˚"
        ],
        "sad depressed": [
            "(◞ ‸ ◟ㆀ)",
            "(╯︵╰)"
        ],
        "seal": [
            "(ᵔᴥᵔ)"
        ],
        "weird awkward": [
            "( ་ ⍸ ་ )",
            "(　☉д⊙)"
        ],
        "weird creepy": [
            "(´⊙◞⊱◟⊙｀)",
            "(´◉◞⊖◟◉｀)"
        ],
        "weird wtf gross": [
            "༼;´༎ຶ ۝ ༎ຶ༽",
            "(◞≼⓪≽◟⋌⋚⋛⋋◞≼⓪≽)"
        ],
        "weird hide stalkerific": [
            "|ω・)",
            "┬┴┬┴┤(･_├┬┴┬┴"
        ]
    },
    "nameMap": {
        // eslint-disable-line
    },
    "nameArr": [
        // eslint-disable-line
    ]
}

const kaomoji = kaomojiCollection.kaomoji

for (const fullName in kaomoji) {
	fullName.split(" ").forEach(partiallyName => {
		while (kaomojiCollection.nameMap[partiallyName]) {
            partiallyName = `_${partiallyName}`
        }
        kaomojiCollection.nameMap[partiallyName] = fullName
        kaomojiCollection.nameArr.push(partiallyName)
	})
}

const nameMap = kaomojiCollection.nameMap
const nameArr = kaomojiCollection.nameArr
<|endoftext|>


<commit_before>import CssNodeExtract from 'css-node-extract';
import fs from 'fs';
import glob from 'glob';
import path from 'path';
import postcssSyntax from 'postcss-scss';

import cleanImportUrl from 'node-sass-filter-importer/dist/lib/clean-import-url';
import extractImportFilters from 'node-sass-filter-importer/dist/lib/extract-import-filters';

import FilterImporter from 'node-sass-filter-importer/dist/lib/filter-importer';
import GlobImporter from 'node-sass-glob-importer/dist/GlobImporter';
import PackageImporter from 'node-sass-package-importer/dist/PackageImporter';
import SelectorImporter from 'node-sass-selector-importer/dist/SelectorImporter';

import defaultOptions from './default-options';

/**
 * Selector specific imports, filter imports, module importing,
 * globbing support and import files only once.
 */
export default class MagicImporter {
  /**
   * @param {Object} options - Configuration options.
   */
  constructor(options = {}) {
    /** @type {Object} */
    this.options = Object.assign({}, defaultOptions, options);
    /** @type {Array} */
    this.store = [];
  }

  /**
   * Find the absolute URL for a given relative URL.
   *
   * @param {String} url
   *   Import url from node-sass.
   * @return {String}
   *   Absolute import url.
   */
  getAbsoluteUrl(url) {
    let absoluteUrl = url;
    if (!path.isAbsolute(url)) {
      this.options.includePaths.some((includePath) => {
        const globMatch = glob.sync(path.join(
          includePath,
          path.parse(url).dir,
          `?(_)${path.parse(url).name}@(${this.options.extensions.join(`|`)})`
        ));

        if (globMatch.length) {
          absoluteUrl = globMatch[0];
          return true;
        }
        return false;
      });
    }
    return absoluteUrl;
  }

  /**
   * Add an URL to the store of imported URLs.
   *
   * @param {String} cleanUrl
   *   Cleaned up import url from node-sass.
   */
  storeAdd(cleanUrl) {
    const absoluteUrl = this.getAbsoluteUrl(cleanUrl);
    if (!this.store.includes(absoluteUrl)) this.store.push(absoluteUrl);
  }

  /**
   * Check if an URL is in store, add it if is not and it has no filters.
   *
   * @param {String} cleanUrl
   *   Cleaned up import url from node-sass.
   * @param {Boolean} hasFilters
   *   Does the URL have filters or not.
   * @return {boolean}
   *   Returns true if the URL has no filters and is already stored.
   */
  isInStore(cleanUrl, hasFilters = false) {
    const absoluteUrl = this.getAbsoluteUrl(cleanUrl);

    if (!hasFilters && this.store.includes(absoluteUrl)) return true;

    if (hasFilters && this.store.includes(absoluteUrl)) {
      if (!this.options.disableWarnings) {
        // eslint-disable-next-line no-console
        console.warn(`Warning: double import of file "${absoluteUrl}".`);
      }
      return false;
    }

    return false;
  }

  /**
   * Synchronously resolve the path to a node-sass import url.
   *
   * @param {String} url
   *   Import url from node-sass.
   * @return {String}
   *   Importer object or null.
   */
  resolveSync(url) {
    let data = null;
    let resolvedUrl = cleanImportUrl(url);

    // Parse url and eventually extract filters.
    const filterNames = extractImportFilters(url);

    // Parse url and eventually extract selector filters.
    const selectorImporter = new SelectorImporter(this.options);
    const selectorFilters = selectorImporter.parseUrl(url).selectorFilters || [];
    const hasFilters = filterNames.length || selectorFilters.length;

    // Try to resolve glob pattern url.
    const globImporter = new GlobImporter(this.options);
    const globFiles = globImporter.resolveFilePathsSync(resolvedUrl);
    if (globFiles.length) {
      return { contents: globFiles.map((globUrl) => {
        if (!this.isInStore(globUrl, hasFilters) || this.options.disableImportOnce) {
          if (!hasFilters) this.storeAdd(globUrl);
          return fs.readFileSync(globUrl, { encoding: `utf8` });
        }
        if (!hasFilters) this.storeAdd(globUrl);
        return ``;
      }).join(`\n`) };
    }

    // Try to resolve a module url.
    const packageImporter = new PackageImporter(this.options);
    const packageImportData = packageImporter.resolveSync(resolvedUrl);
    if (packageImportData) {
      resolvedUrl = packageImportData.file;
      data = { file: resolvedUrl };
    }

    // If the file is already stored and should not be loaded,
    // prevent node-sass from importing the file again.
    if (this.isInStore(resolvedUrl, hasFilters) && !this.options.disableImportOnce) {
      return {
        file: ``,
        contents: ``,
      };
    }

    if (!hasFilters) this.storeAdd(resolvedUrl);

    // Filter.
    let filteredContents;
    // @TODO: This is ugly, maybe refactor.
    if (selectorFilters.length) {
      filteredContents = selectorImporter.extractSelectors(resolvedUrl, selectorFilters);
    }
    if (filterNames.length) {
      if (filteredContents) {
        filteredContents = CssNodeExtract.processSync({
          css: filteredContents,
          filterNames,
          postcssSyntax,
        });
      } else {
        const filterImporter = new FilterImporter(this.options);
        filteredContents = filterImporter.extractFilters(resolvedUrl, filterNames);
      }
    }
    if (filteredContents) {
      data = {
        file: resolvedUrl,
        contents: filteredContents,
      };
    }

    return data;
  }

  /**
   * Asynchronously resolve the path to a node-sass import url.
   *
   * @param {string} url
   *   Import url from node-sass.
   * @return {Promise}
   *   Promise for importer object or null.
   */
  resolve(url) {
    return new Promise((promiseResolve) => {
      promiseResolve(this.resolveSync(url));
    });
  }
}
<commit_msg>Replace the getAbsoluteUrl method with resolveUrl.<commit_after>import CssNodeExtract from 'css-node-extract';
import fs from 'fs';
import postcssSyntax from 'postcss-scss';

import cleanImportUrl from 'node-sass-filter-importer/dist/lib/clean-import-url';
import extractImportFilters from 'node-sass-filter-importer/dist/lib/extract-import-filters';

import FilterImporter from 'node-sass-filter-importer/dist/lib/filter-importer';
import GlobImporter from 'node-sass-glob-importer/dist/GlobImporter';
import PackageImporter from 'node-sass-package-importer/dist/PackageImporter';
import SelectorImporter from 'node-sass-selector-importer/dist/SelectorImporter';

import defaultOptions from './default-options';
import resolveUrl from './resolve-url';

/**
 * Selector specific imports, filter imports, module importing,
 * globbing support and import files only once.
 */
export default class MagicImporter {
  /**
   * @param {Object} options - Configuration options.
   */
  constructor(options = {}) {
    /** @type {Object} */
    this.options = Object.assign({}, defaultOptions, options);
    /** @type {Array} */
    this.store = [];
  }

  /**
   * Add an URL to the store of imported URLs.
   *
   * @param {String} cleanUrl
   *   Cleaned up import url from node-sass.
   */
  storeAdd(cleanUrl) {
    const resolvedUrl = resolveUrl(cleanUrl);
    if (!this.store.includes(resolvedUrl)) this.store.push(resolvedUrl);
  }

  /**
   * Check if an URL is in store, add it if is not and it has no filters.
   *
   * @param {String} cleanUrl
   *   Cleaned up import url from node-sass.
   * @param {Boolean} hasFilters
   *   Does the URL have filters or not.
   * @return {boolean}
   *   Returns true if the URL has no filters and is already stored.
   */
  isInStore(cleanUrl, hasFilters = false) {
    const resolvedUrl = resolveUrl(cleanUrl);

    if (!hasFilters && this.store.includes(resolvedUrl)) return true;

    if (hasFilters && this.store.includes(resolvedUrl)) {
      if (!this.options.disableWarnings) {
        // eslint-disable-next-line no-console
        console.warn(`Warning: double import of file "${resolvedUrl}".`);
      }
      return false;
    }

    return false;
  }

  /**
   * Synchronously resolve the path to a node-sass import url.
   *
   * @param {String} url
   *   Import url from node-sass.
   * @return {String}
   *   Importer object or null.
   */
  resolveSync(url) {
    let data = null;
    let resolvedUrl = cleanImportUrl(url);

    // Parse url and eventually extract filters.
    const filterNames = extractImportFilters(url);

    // Parse url and eventually extract selector filters.
    const selectorImporter = new SelectorImporter(this.options);
    const selectorFilters = selectorImporter.parseUrl(url).selectorFilters || [];
    const hasFilters = filterNames.length || selectorFilters.length;

    // Try to resolve glob pattern url.
    const globImporter = new GlobImporter(this.options);
    const globFiles = globImporter.resolveFilePathsSync(resolvedUrl);
    if (globFiles.length) {
      return { contents: globFiles.map((globUrl) => {
        if (!this.isInStore(globUrl, hasFilters) || this.options.disableImportOnce) {
          if (!hasFilters) this.storeAdd(globUrl);
          return fs.readFileSync(globUrl, { encoding: `utf8` });
        }
        if (!hasFilters) this.storeAdd(globUrl);
        return ``;
      }).join(`\n`) };
    }

    // Try to resolve a module url.
    const packageImporter = new PackageImporter(this.options);
    const packageImportData = packageImporter.resolveSync(resolvedUrl);
    if (packageImportData) {
      resolvedUrl = packageImportData.file;
      data = { file: resolvedUrl };
    }

    // If the file is already stored and should not be loaded,
    // prevent node-sass from importing the file again.
    if (this.isInStore(resolvedUrl, hasFilters) && !this.options.disableImportOnce) {
      return {
        file: ``,
        contents: ``,
      };
    }

    if (!hasFilters) this.storeAdd(resolvedUrl);

    // Filter.
    let filteredContents;
    // @TODO: This is ugly, maybe refactor.
    if (selectorFilters.length) {
      filteredContents = selectorImporter.extractSelectors(resolvedUrl, selectorFilters);
    }
    if (filterNames.length) {
      if (filteredContents) {
        filteredContents = CssNodeExtract.processSync({
          css: filteredContents,
          filterNames,
          postcssSyntax,
        });
      } else {
        const filterImporter = new FilterImporter(this.options);
        filteredContents = filterImporter.extractFilters(resolvedUrl, filterNames);
      }
    }
    if (filteredContents) {
      data = {
        file: resolvedUrl,
        contents: filteredContents,
      };
    }

    return data;
  }

  /**
   * Asynchronously resolve the path to a node-sass import url.
   *
   * @param {string} url
   *   Import url from node-sass.
   * @return {Promise}
   *   Promise for importer object or null.
   */
  resolve(url) {
    return new Promise((promiseResolve) => {
      promiseResolve(this.resolveSync(url));
    });
  }
}
<|endoftext|>


<commit_before>'use strict';

const electron = require('electron');
const { getConfigField, updateConfigField } = require('../config.js');
const AutoLaunch = require('auto-launch');
const isMac = require('../utils/misc.js').isMac;
const log = require('../log.js');
const logLevels = require('../enums/logLevels.js');
const eventEmitter = require('../eventEmitter');

let minimizeOnClose = false;
let launchOnStartup = false;
let isAlwaysOnTop = false;

setCheckboxValues();

let symphonyAutoLauncher;

if (isMac) {
    symphonyAutoLauncher = new AutoLaunch({
        name: 'Symphony',
        mac: {
            useLaunchAgent: true,
        },
        path: process.execPath,
    });
} else {
    symphonyAutoLauncher = new AutoLaunch({
        name: 'Symphony',
        path: process.execPath,
    });
}

const template = [{
    label: 'Edit',
    submenu: [
            { role: 'undo' },
            { role: 'redo' },
            { type: 'separator' },
            { role: 'cut' },
            { role: 'copy' },
            { role: 'paste' },
            { role: 'pasteandmatchstyle' },
            { role: 'delete' },
            { role: 'selectall' }
    ]
},
{
    label: 'View',
    submenu: [{
        label: 'Reload',
        accelerator: 'CmdOrCtrl+R',
        click(item, focusedWindow) {
            if (focusedWindow) {
                focusedWindow.reload();
            }
        }
    },
    {
        label: 'Toggle Developer Tools',
        accelerator: isMac ? 'Alt+Command+I' : 'Ctrl+Shift+I',
        click(item, focusedWindow) {
            if (focusedWindow) {
                focusedWindow.webContents.toggleDevTools();
            }
        }
    },
    {
        label: 'Open Crashes Directory',
        click() { electron.shell.showItemInFolder(electron.crashReporter.getCrashesDirectory()); }
    },
    {
        type: 'separator'
    },
    {
        role: 'resetzoom'
    },
    {
        role: 'zoomin'
    },
    {
        role: 'zoomout'
    },
    {
        type: 'separator'
    },
    {
        role: 'togglefullscreen'
    }
    ]
},
{
    role: 'window',
    submenu: [{
        role: 'minimize'
    },
    {
        role: 'close'
    }
    ]
},
{
    role: 'help',
    submenu: [
        {
            label: 'Learn More',
            click() { electron.shell.openExternal('https://www.symphony.com'); }
        }]
}
];

function getTemplate(app) {
    if (isMac && template[0].label !== app.getName()) {
        template.unshift({
            label: app.getName(),
            submenu: [{
                role: 'about'
            },
            {
                type: 'separator'
            },
            {
                role: 'services',
                submenu: []
            },
            {
                type: 'separator'
            },
            {
                role: 'hide'
            },
            {
                role: 'hideothers'
            },
            {
                role: 'unhide'
            },
            {
                type: 'separator'
            },
            {
                role: 'quit'
            }
            ]
        });
        // Edit menu.
        template[1].submenu.push({
            type: 'separator'
        }, {
            label: 'Speech',
            submenu: [{
                role: 'startspeaking'
            },
            {
                role: 'stopspeaking'
            }
            ]
        });
        // Window menu.
        template[3].submenu = [{
            label: 'Close',
            accelerator: 'CmdOrCtrl+W',
            role: 'close'
        },
        {
            label: 'Minimize',
            accelerator: 'CmdOrCtrl+M',
            role: 'minimize'
        },
        {
            label: 'Zoom',
            role: 'zoom'
        },
        {
            type: 'separator'
        },
        {
            label: 'Bring All to Front',
            role: 'front'
        }
        ];
    }

    let index = 2;
    if (isMac && template[0].label !== app.getName()) {
        index = 3;
    }

    // Window menu -> launchOnStartup.
    template[index].submenu.push({
        label: 'Auto Launch On Startup',
        type: 'checkbox',
        checked: launchOnStartup,
        click: function(item) {
            if (item.checked) {
                symphonyAutoLauncher.enable()
                    .catch(function(err) {
                        let title = 'Error setting AutoLaunch configuration';
                        log.send(logLevels.ERROR, 'MenuTemplate: ' + title + ': auto launch error ' + err);
                        electron.dialog.showErrorBox(title, title + ': ' + err);
                    });
            } else {
                symphonyAutoLauncher.disable()
                    .catch(function(err) {
                        let title = 'Error setting AutoLaunch configuration';
                        log.send(logLevels.ERROR, 'MenuTemplate: ' + title + ': auto launch error ' + err);
                        electron.dialog.showErrorBox(title, title + ': ' + err);
                    });
            }
            launchOnStartup = item.checked;
            updateConfigField('launchOnStartup', launchOnStartup);
        }
    });

    // Window menu -> alwaysOnTop.
    template[index].submenu.push({
        label: 'Always on top',
        type: 'checkbox',
        checked: isAlwaysOnTop,
        click: (item) => {
            isAlwaysOnTop = item.checked;
            eventEmitter.emit('isAlwaysOnTop', isAlwaysOnTop);
            updateConfigField('alwaysOnTop', isAlwaysOnTop);
        }
    });

    // Window menu -> minimizeOnClose.
    // ToDo: Add behavior on Close.
    template[index].submenu.push({
        label: 'Minimize on Close',
        type: 'checkbox',
        checked: minimizeOnClose,
        click: function(item) {
            minimizeOnClose = item.checked;
            updateConfigField('minimizeOnClose', minimizeOnClose);
        }
    });

    if (!isMac) {
        template[index].submenu.push({
            label: 'Quit Symphony',
            click: function() {
                app.quit();
            }
        });
    }

    return template;
}

/**
 * Sets the checkbox values for different menu items
 * based on configuration
 */
function setCheckboxValues() {
    getConfigField('minimizeOnClose').then(function(mClose) {
        minimizeOnClose = mClose;
    }).catch(function(err) {
        let title = 'Error loading configuration';
        log.send(logLevels.ERROR, 'MenuTemplate: error getting config field minimizeOnClose, error: ' + err);
        electron.dialog.showErrorBox(title, title + ': ' + err);
    });

    getConfigField('launchOnStartup').then(function(lStartup) {
        launchOnStartup = lStartup;
    }).catch(function(err) {
        let title = 'Error loading configuration';
        log.send(logLevels.ERROR, 'MenuTemplate: error getting config field launchOnStartup, error: ' + err);
        electron.dialog.showErrorBox(title, title + ': ' + err);
    });

    getConfigField('alwaysOnTop').then(function(mAlwaysOnTop) {
        isAlwaysOnTop = mAlwaysOnTop;
        eventEmitter.emit('isAlwaysOnTop', isAlwaysOnTop);
    }).catch(function(err) {
        let title = 'Error loading configuration';
        log.send(logLevels.ERROR, 'MenuTemplate: error getting config field alwaysOnTop, error: ' + err);
        electron.dialog.showErrorBox(title, title + ': ' + err);
    });

    getConfigField('notificationSettings').then(function(notfObject) {
        eventEmitter.emit('notificationSettings', notfObject);
    }).catch(function(err) {
        let title = 'Error loading configuration';
        log.send(logLevels.ERROR, 'MenuTemplate: error getting config field notificationSettings, error: ' + err);
        electron.dialog.showErrorBox(title, title + ': ' + err);
    });

}

function getMinimizeOnClose() {
    return minimizeOnClose;
}

module.exports = {
    getTemplate: getTemplate,
    getMinimizeOnClose: getMinimizeOnClose
};<commit_msg>electron-17: added logic to open the completed directory under the crashes directory<commit_after>'use strict';

const electron = require('electron');
const { getConfigField, updateConfigField } = require('../config.js');
const AutoLaunch = require('auto-launch');
const isMac = require('../utils/misc.js').isMac;
const log = require('../log.js');
const logLevels = require('../enums/logLevels.js');
const eventEmitter = require('../eventEmitter');

let minimizeOnClose = false;
let launchOnStartup = false;
let isAlwaysOnTop = false;

setCheckboxValues();

let symphonyAutoLauncher;

if (isMac) {
    symphonyAutoLauncher = new AutoLaunch({
        name: 'Symphony',
        mac: {
            useLaunchAgent: true,
        },
        path: process.execPath,
    });
} else {
    symphonyAutoLauncher = new AutoLaunch({
        name: 'Symphony',
        path: process.execPath,
    });
}

const template = [{
    label: 'Edit',
    submenu: [
            { role: 'undo' },
            { role: 'redo' },
            { type: 'separator' },
            { role: 'cut' },
            { role: 'copy' },
            { role: 'paste' },
            { role: 'pasteandmatchstyle' },
            { role: 'delete' },
            { role: 'selectall' }
    ]
},
{
    label: 'View',
    submenu: [{
        label: 'Reload',
        accelerator: 'CmdOrCtrl+R',
        click(item, focusedWindow) {
            if (focusedWindow) {
                focusedWindow.reload();
            }
        }
    },
    {
        label: 'Toggle Developer Tools',
        accelerator: isMac ? 'Alt+Command+I' : 'Ctrl+Shift+I',
        click(item, focusedWindow) {
            if (focusedWindow) {
                focusedWindow.webContents.toggleDevTools();
            }
        }
    },
    {
        label: 'Open Crashes Directory',
        click() {
            const crashesDirectory = electron.crashReporter.getCrashesDirectory() + '/completed';
            electron.shell.showItemInFolder(crashesDirectory);
        }
    },
    {
        type: 'separator'
    },
    {
        role: 'resetzoom'
    },
    {
        role: 'zoomin'
    },
    {
        role: 'zoomout'
    },
    {
        type: 'separator'
    },
    {
        role: 'togglefullscreen'
    }
    ]
},
{
    role: 'window',
    submenu: [{
        role: 'minimize'
    },
    {
        role: 'close'
    }
    ]
},
{
    role: 'help',
    submenu: [
        {
            label: 'Learn More',
            click() { electron.shell.openExternal('https://www.symphony.com'); }
        }]
}
];

function getTemplate(app) {
    if (isMac && template[0].label !== app.getName()) {
        template.unshift({
            label: app.getName(),
            submenu: [{
                role: 'about'
            },
            {
                type: 'separator'
            },
            {
                role: 'services',
                submenu: []
            },
            {
                type: 'separator'
            },
            {
                role: 'hide'
            },
            {
                role: 'hideothers'
            },
            {
                role: 'unhide'
            },
            {
                type: 'separator'
            },
            {
                role: 'quit'
            }
            ]
        });
        // Edit menu.
        template[1].submenu.push({
            type: 'separator'
        }, {
            label: 'Speech',
            submenu: [{
                role: 'startspeaking'
            },
            {
                role: 'stopspeaking'
            }
            ]
        });
        // Window menu.
        template[3].submenu = [{
            label: 'Close',
            accelerator: 'CmdOrCtrl+W',
            role: 'close'
        },
        {
            label: 'Minimize',
            accelerator: 'CmdOrCtrl+M',
            role: 'minimize'
        },
        {
            label: 'Zoom',
            role: 'zoom'
        },
        {
            type: 'separator'
        },
        {
            label: 'Bring All to Front',
            role: 'front'
        }
        ];
    }

    let index = 2;
    if (isMac && template[0].label !== app.getName()) {
        index = 3;
    }

    // Window menu -> launchOnStartup.
    template[index].submenu.push({
        label: 'Auto Launch On Startup',
        type: 'checkbox',
        checked: launchOnStartup,
        click: function(item) {
            if (item.checked) {
                symphonyAutoLauncher.enable()
                    .catch(function(err) {
                        let title = 'Error setting AutoLaunch configuration';
                        log.send(logLevels.ERROR, 'MenuTemplate: ' + title + ': auto launch error ' + err);
                        electron.dialog.showErrorBox(title, title + ': ' + err);
                    });
            } else {
                symphonyAutoLauncher.disable()
                    .catch(function(err) {
                        let title = 'Error setting AutoLaunch configuration';
                        log.send(logLevels.ERROR, 'MenuTemplate: ' + title + ': auto launch error ' + err);
                        electron.dialog.showErrorBox(title, title + ': ' + err);
                    });
            }
            launchOnStartup = item.checked;
            updateConfigField('launchOnStartup', launchOnStartup);
        }
    });

    // Window menu -> alwaysOnTop.
    template[index].submenu.push({
        label: 'Always on top',
        type: 'checkbox',
        checked: isAlwaysOnTop,
        click: (item) => {
            isAlwaysOnTop = item.checked;
            eventEmitter.emit('isAlwaysOnTop', isAlwaysOnTop);
            updateConfigField('alwaysOnTop', isAlwaysOnTop);
        }
    });

    // Window menu -> minimizeOnClose.
    // ToDo: Add behavior on Close.
    template[index].submenu.push({
        label: 'Minimize on Close',
        type: 'checkbox',
        checked: minimizeOnClose,
        click: function(item) {
            minimizeOnClose = item.checked;
            updateConfigField('minimizeOnClose', minimizeOnClose);
        }
    });

    if (!isMac) {
        template[index].submenu.push({
            label: 'Quit Symphony',
            click: function() {
                app.quit();
            }
        });
    }

    return template;
}

/**
 * Sets the checkbox values for different menu items
 * based on configuration
 */
function setCheckboxValues() {
    getConfigField('minimizeOnClose').then(function(mClose) {
        minimizeOnClose = mClose;
    }).catch(function(err) {
        let title = 'Error loading configuration';
        log.send(logLevels.ERROR, 'MenuTemplate: error getting config field minimizeOnClose, error: ' + err);
        electron.dialog.showErrorBox(title, title + ': ' + err);
    });

    getConfigField('launchOnStartup').then(function(lStartup) {
        launchOnStartup = lStartup;
    }).catch(function(err) {
        let title = 'Error loading configuration';
        log.send(logLevels.ERROR, 'MenuTemplate: error getting config field launchOnStartup, error: ' + err);
        electron.dialog.showErrorBox(title, title + ': ' + err);
    });

    getConfigField('alwaysOnTop').then(function(mAlwaysOnTop) {
        isAlwaysOnTop = mAlwaysOnTop;
        eventEmitter.emit('isAlwaysOnTop', isAlwaysOnTop);
    }).catch(function(err) {
        let title = 'Error loading configuration';
        log.send(logLevels.ERROR, 'MenuTemplate: error getting config field alwaysOnTop, error: ' + err);
        electron.dialog.showErrorBox(title, title + ': ' + err);
    });

    getConfigField('notificationSettings').then(function(notfObject) {
        eventEmitter.emit('notificationSettings', notfObject);
    }).catch(function(err) {
        let title = 'Error loading configuration';
        log.send(logLevels.ERROR, 'MenuTemplate: error getting config field notificationSettings, error: ' + err);
        electron.dialog.showErrorBox(title, title + ': ' + err);
    });

}

function getMinimizeOnClose() {
    return minimizeOnClose;
}

module.exports = {
    getTemplate: getTemplate,
    getMinimizeOnClose: getMinimizeOnClose
};<|endoftext|>