<commit_before>Oskari.registerLocalization(
    {
        "lang": "sv",
        "key": "Publisher",
        "value": {
            "title": "Definiera karta",
            "flyouttitle": "Definiera karta",
            "desc": "",
            "published": {
                "title": "Kartan har avskilts",
                "desc": "Inbädda kartan genom att tillägga HTML-koden nedan på din webbplats.",
                "urlPrefix": "http://www.paikkatietoikkuna.fi"
            },
            "edit" : {
              "popup" : {
                  "title": "Den inbäddade kartan editeras",
                  "msg": "Den tidigare inbäddade kartans uppgifter uppdateras"
              }
            },
            "BasicView": {
                "title": "Inbädda kartan",
                "titleEdit" : "Editera inbäddad karta",
                "domain": {
                    "title": "Webbsidan där kartan inbäddas",
                    "label": "Webbplatsen där kartan inbäddas",
                    "placeholder": "utan http- eller www-prefix",
                    "tooltip": "Skriv namnet på webbplatsens hemsida dvs. domainnamn utan prefixerna http och www samt utan undersida. Exempel: minhemsida.com"
                },
                "name": {
                    "label": "Kartans namn",
                    "placeholder": "obligatorisk uppgift",
                    "tooltip": "Ge kartan ett beskrivande namn. Observera användargränssnittets språk"
                },
                "language": {
                    "label": "Språk",
                    "options": {
                        "fi": "Finska",
                        "sv": "Svenska",
                        "en": "Engelska"
                    },
                    "tooltip": "Välj språk för kartmaterial och användargränssnitt."
                },
                "size": {
                    "label": "Storlek",
                    "tooltip": "Välj eller definiera storleken på kartan som visas på din webbplats. Kartan förhandsvisas i den valda storleken."
                },
                "tools": {
                    "label": "Verktyg",
                    "tooltip": "Välj verktygen som visas på kartan. Du kan se deras placering på den förhandsvisade kartan.",
                    "ScaleBarPlugin": "Skalsträcka",
                    "IndexMapPlugin": "Indexkarta",
                    "PanButtons" : "Panoreringsverktyg",
                    "Portti2Zoombar": "Skalans glidreglage",
                    "ControlsPlugin": "Panorering på",
                    "SearchPlugin": "Adress- och ortnamnssökning",
                    "GetInfoPlugin": "Verktyg för förfrågan som gäller uppgifter om objektet"
                },
                "data": {
                    "label": "Satistik",
                    "tooltip": "Show the data related to map.",
                    "grid" : "Visa statistik bord"
                },
                "layout": {
                    "label": "Ulkoasu",
                    "fields": {
                        "colours": {
                            "label": "Värimaailma",
                            "placeholder": "Valitse värimaailma",
                            "buttonLabel": "Vaihda",
                            "light_grey": "Vaalean harmaa",
                            "dark_grey": "Tumman harmaa",
                            "blue": "Sininen",
                            "red": "Punainen",
                            "green": "Vihreä",
                            "yellow": "Keltainen",
                            "custom": {
                                "name": "Omat värit",
                                "bgLabel": "Tunnisteen tausta",
                                "titleLabel": "Tunnisteen teksti",
                                "headerLabel": "Otsikon teksti",
                                "iconLabel": "Ikoni",
                                "iconCloseLabel": "Tumma",
                                "iconCloseWhiteLabel": "Vaalea"
                            }
                        },
                        "fonts": {
                            "label": "Valitse fontti"
                        },
                        "toolStyles": {
                            "label": "Työkalujen tyyli",
                            "rounded-dark": "Pyöristetty (tumma)",
                            "rounded-light": "Pyöristetty (vaalea)",
                            "sharp-dark": "Kulmikas (tumma)",
                            "sharp-light": "Kulmikas (vaalea)",
                            "3d-dark": "3D (tumma)",
                            "3d-light": "3D (vaalea)"
                        }
                    },
                    "popup": {
                        "title": "Värimaailman valinta",
                        "close": "Sulje",
                        "gfiDialog": {
                            "title": "Esikatselu",
                            "featureName": "Esikatselu",
                            "featureDesc": "Värimaailman valinta vaikuttaa ainoastaan kohdetietolaatikon väritykseen"
                        }
                    }
                },
                "layers": {
                    "label": "Kartlager",
                    "defaultLayer": "(Förvald kartlager)",
                    "useAsDefaultLayer": "Använd som förvald kartlager"
                },
                "sizes": {
                    "small": "Liten",
                    "medium": "Medelstor",
                    "large": "Stor",
                    "custom": "Definiera storlek",
                    "width": "bredd",
                    "height": "höjd"
                },
                "buttons": {
                    "save": "Lagra",
                    "saveNew": "Lagra ny",
                    "ok": "OK",
                    "replace": "Ersätt",
                    "cancel": "Avbryt"
                },
                "confirm" : {
                    "replace" : {
                        "title" : "Vill du ersätta den inbäddade kartan?",
                        "msg" : "Om du ersätter kartan syns ändringarna på den inbäddade kartan genast. Du behöver inte lägga in koden på din webbsida på nytt."
                    }
                },
                "layerselection": {
                    "label": "Visa kartlagren i menyn",
                    "info": "Välj bakgrundskartor. Du kan göra förval i förhandsgranskningsvyn.",
                    "tooltip": "Bakgrundskartan syns som kartans nedersta lager. När du väljer kartan som används som bakgrundskarta syns endast ett lager i taget och du kan växla mellan dem. Du kan göra förval i förhandsgranskningsvyn.",
                    "promote": "Visa flygbilder?"
                },
                "preview": "Förhandsgranskningsvy för karta som ska avskiljas och inbäddas.",
                "location": "Läge och skalnivå.",
                "zoomlevel": "Skalnivå",
                "help": "Anvisning",
                "error": {
                    "title": "Fel!",
                    "size": "Fel i storleksdefinitionerna",
                    "domain": "Webbplatsen är en nödvändig uppgift",
                    "domainStart": "Skriv webbplatsen utan prefixerna http och www",
                    "name": "Namnet är en nödvändig uppgift",
                    "nohelp": "Ingen anvisning",
                    "saveFailed": "Avskiljandet av kartan misslyckades. Försök på nytt senare.",
                    "nameIllegalCharacters": "I namnet ingår otillåtna tecken. Tillåtna är alla bokstäver i det svenska alfabetet, siffror, mellanslag och bindestreck.",
                    "domainIllegalCharacters": "I webbsidans namnet ingår otillåtna tecken. Tillåtna är alla bokstäver i det svenska alfabetet, siffror, mellanslag och bindestreck."
                }
            },
            "NotLoggedView": {
                "text": "Logga in i tjänsten för att definiera en karta som ska inbäddas.",
                "signup": "Logga in",
                "signupUrl": "/web/sv/login",
                "register": "Registrera dig",
                "registerUrl": "/web/sv/login?p_p_id=58&p_p_lifecycle=1&p_p_state=maximized&p_p_mode=view&p_p_col_id=column-1&p_p_col_count=1&saveLastPath=0&_58_struts_action=%2Flogin%2Fcreate_account"
            },
            "StartView": {
                "text": "Du kan inbädda den kartvy som du definierat här på din egen eller din arbetsgivares webbplats.",
                "touLink": "Vis användningsvillkor till kartinbäddningen",
                "layerlist_title": "Kartlager som kan inbäddas",
                "layerlist_empty": "Kartlagren som du valt kan inte avskiljas. Menyn Valda kartlager visar vilka kartlager kan avskiljas.",
                "layerlist_denied": "Kan inte avskiljas",
                "denied_tooltip": "Kartdataproducenterna har inte gett publiceringstillstånd till dessa material i andra webbtjänster. Kontrollera publiceringstillståndet i menyn Valda kartlager innan du avskiljer dem.",
                "myPlacesDisclaimer": "Obs! du ska publicera din egen kartlagret."
                "buttons": {
                    "continue": "Fortsätt",
                    "continueAndAccept": "Godkänn användningsvillkor och fortsätt",
                    "cancel": "Tillbaka",
                    "close" : "Avbryt"
                },
                "tou" : {
                    "notfound" : "Användningsvillkor kan inte hittas",
                    "reject" : "Avvisa",
                    "accept" : "Acceptera"
                }
            }
        }
    });<commit_msg>Added missing comma to sv locale<commit_after>Oskari.registerLocalization(
    {
        "lang": "sv",
        "key": "Publisher",
        "value": {
            "title": "Definiera karta",
            "flyouttitle": "Definiera karta",
            "desc": "",
            "published": {
                "title": "Kartan har avskilts",
                "desc": "Inbädda kartan genom att tillägga HTML-koden nedan på din webbplats.",
                "urlPrefix": "http://www.paikkatietoikkuna.fi"
            },
            "edit" : {
              "popup" : {
                  "title": "Den inbäddade kartan editeras",
                  "msg": "Den tidigare inbäddade kartans uppgifter uppdateras"
              }
            },
            "BasicView": {
                "title": "Inbädda kartan",
                "titleEdit" : "Editera inbäddad karta",
                "domain": {
                    "title": "Webbsidan där kartan inbäddas",
                    "label": "Webbplatsen där kartan inbäddas",
                    "placeholder": "utan http- eller www-prefix",
                    "tooltip": "Skriv namnet på webbplatsens hemsida dvs. domainnamn utan prefixerna http och www samt utan undersida. Exempel: minhemsida.com"
                },
                "name": {
                    "label": "Kartans namn",
                    "placeholder": "obligatorisk uppgift",
                    "tooltip": "Ge kartan ett beskrivande namn. Observera användargränssnittets språk"
                },
                "language": {
                    "label": "Språk",
                    "options": {
                        "fi": "Finska",
                        "sv": "Svenska",
                        "en": "Engelska"
                    },
                    "tooltip": "Välj språk för kartmaterial och användargränssnitt."
                },
                "size": {
                    "label": "Storlek",
                    "tooltip": "Välj eller definiera storleken på kartan som visas på din webbplats. Kartan förhandsvisas i den valda storleken."
                },
                "tools": {
                    "label": "Verktyg",
                    "tooltip": "Välj verktygen som visas på kartan. Du kan se deras placering på den förhandsvisade kartan.",
                    "ScaleBarPlugin": "Skalsträcka",
                    "IndexMapPlugin": "Indexkarta",
                    "PanButtons" : "Panoreringsverktyg",
                    "Portti2Zoombar": "Skalans glidreglage",
                    "ControlsPlugin": "Panorering på",
                    "SearchPlugin": "Adress- och ortnamnssökning",
                    "GetInfoPlugin": "Verktyg för förfrågan som gäller uppgifter om objektet"
                },
                "data": {
                    "label": "Satistik",
                    "tooltip": "Show the data related to map.",
                    "grid" : "Visa statistik bord"
                },
                "layout": {
                    "label": "Ulkoasu",
                    "fields": {
                        "colours": {
                            "label": "Värimaailma",
                            "placeholder": "Valitse värimaailma",
                            "buttonLabel": "Vaihda",
                            "light_grey": "Vaalean harmaa",
                            "dark_grey": "Tumman harmaa",
                            "blue": "Sininen",
                            "red": "Punainen",
                            "green": "Vihreä",
                            "yellow": "Keltainen",
                            "custom": {
                                "name": "Omat värit",
                                "bgLabel": "Tunnisteen tausta",
                                "titleLabel": "Tunnisteen teksti",
                                "headerLabel": "Otsikon teksti",
                                "iconLabel": "Ikoni",
                                "iconCloseLabel": "Tumma",
                                "iconCloseWhiteLabel": "Vaalea"
                            }
                        },
                        "fonts": {
                            "label": "Valitse fontti"
                        },
                        "toolStyles": {
                            "label": "Työkalujen tyyli",
                            "rounded-dark": "Pyöristetty (tumma)",
                            "rounded-light": "Pyöristetty (vaalea)",
                            "sharp-dark": "Kulmikas (tumma)",
                            "sharp-light": "Kulmikas (vaalea)",
                            "3d-dark": "3D (tumma)",
                            "3d-light": "3D (vaalea)"
                        }
                    },
                    "popup": {
                        "title": "Värimaailman valinta",
                        "close": "Sulje",
                        "gfiDialog": {
                            "title": "Esikatselu",
                            "featureName": "Esikatselu",
                            "featureDesc": "Värimaailman valinta vaikuttaa ainoastaan kohdetietolaatikon väritykseen"
                        }
                    }
                },
                "layers": {
                    "label": "Kartlager",
                    "defaultLayer": "(Förvald kartlager)",
                    "useAsDefaultLayer": "Använd som förvald kartlager"
                },
                "sizes": {
                    "small": "Liten",
                    "medium": "Medelstor",
                    "large": "Stor",
                    "custom": "Definiera storlek",
                    "width": "bredd",
                    "height": "höjd"
                },
                "buttons": {
                    "save": "Lagra",
                    "saveNew": "Lagra ny",
                    "ok": "OK",
                    "replace": "Ersätt",
                    "cancel": "Avbryt"
                },
                "confirm" : {
                    "replace" : {
                        "title" : "Vill du ersätta den inbäddade kartan?",
                        "msg" : "Om du ersätter kartan syns ändringarna på den inbäddade kartan genast. Du behöver inte lägga in koden på din webbsida på nytt."
                    }
                },
                "layerselection": {
                    "label": "Visa kartlagren i menyn",
                    "info": "Välj bakgrundskartor. Du kan göra förval i förhandsgranskningsvyn.",
                    "tooltip": "Bakgrundskartan syns som kartans nedersta lager. När du väljer kartan som används som bakgrundskarta syns endast ett lager i taget och du kan växla mellan dem. Du kan göra förval i förhandsgranskningsvyn.",
                    "promote": "Visa flygbilder?"
                },
                "preview": "Förhandsgranskningsvy för karta som ska avskiljas och inbäddas.",
                "location": "Läge och skalnivå.",
                "zoomlevel": "Skalnivå",
                "help": "Anvisning",
                "error": {
                    "title": "Fel!",
                    "size": "Fel i storleksdefinitionerna",
                    "domain": "Webbplatsen är en nödvändig uppgift",
                    "domainStart": "Skriv webbplatsen utan prefixerna http och www",
                    "name": "Namnet är en nödvändig uppgift",
                    "nohelp": "Ingen anvisning",
                    "saveFailed": "Avskiljandet av kartan misslyckades. Försök på nytt senare.",
                    "nameIllegalCharacters": "I namnet ingår otillåtna tecken. Tillåtna är alla bokstäver i det svenska alfabetet, siffror, mellanslag och bindestreck.",
                    "domainIllegalCharacters": "I webbsidans namnet ingår otillåtna tecken. Tillåtna är alla bokstäver i det svenska alfabetet, siffror, mellanslag och bindestreck."
                }
            },
            "NotLoggedView": {
                "text": "Logga in i tjänsten för att definiera en karta som ska inbäddas.",
                "signup": "Logga in",
                "signupUrl": "/web/sv/login",
                "register": "Registrera dig",
                "registerUrl": "/web/sv/login?p_p_id=58&p_p_lifecycle=1&p_p_state=maximized&p_p_mode=view&p_p_col_id=column-1&p_p_col_count=1&saveLastPath=0&_58_struts_action=%2Flogin%2Fcreate_account"
            },
            "StartView": {
                "text": "Du kan inbädda den kartvy som du definierat här på din egen eller din arbetsgivares webbplats.",
                "touLink": "Vis användningsvillkor till kartinbäddningen",
                "layerlist_title": "Kartlager som kan inbäddas",
                "layerlist_empty": "Kartlagren som du valt kan inte avskiljas. Menyn Valda kartlager visar vilka kartlager kan avskiljas.",
                "layerlist_denied": "Kan inte avskiljas",
                "denied_tooltip": "Kartdataproducenterna har inte gett publiceringstillstånd till dessa material i andra webbtjänster. Kontrollera publiceringstillståndet i menyn Valda kartlager innan du avskiljer dem.",
                "myPlacesDisclaimer": "Obs! du ska publicera din egen kartlagret.",
                "buttons": {
                    "continue": "Fortsätt",
                    "continueAndAccept": "Godkänn användningsvillkor och fortsätt",
                    "cancel": "Tillbaka",
                    "close" : "Avbryt"
                },
                "tou" : {
                    "notfound" : "Användningsvillkor kan inte hittas",
                    "reject" : "Avvisa",
                    "accept" : "Acceptera"
                }
            }
        }
    });<|endoftext|>


<commit_before>Oskari.clazz.define('Oskari.geojson.bundle.geojson.GeoJSONPlugin',
  function (conf, state) {
    var me = this;
    me._clazz = 'Oskari.geojson.bundle.geojson.GeoJSONPlugin';
    me._name = 'MarkersPlugin';
    me.state = state;
  }, {
    hasUI: function () {
      return true;
    },
    _createEventHandlers: function () {
      var me = this;

      return {
        AfterRearrangeSelectedMapLayerEvent: function (event) {
          me.raiseLayer();
        }
      };
    },
    _createRequestHandlers: function () {
      var me = this;
      return {
        'GeoJSONPlugin.AddGeoJSONRequest': Oskari.clazz.create(
          'Oskari.geojson.bundle.geojson.request.AddGeoJSONRequestHandler',
          me
        ),
        'GeoJSONPlugin.RemoveGeoJSONRequest': Oskari.clazz.create(
          'Oskari.geojson.bundle.geojson.request.RemoveGeoJSONRequestHandler',
          me
        )
      };
    },
    register: function () {
      this.getMapModule().setLayerPlugin('geojson', this);
    },
    unregister: function () {
      this.getMapModule().setLayerPlugin('geojson', null);
    },
    _startPluginImpl: function () {
      var me = this;
      me._createGeoJSONLayer();
    },
    _createGeoJSONLayer: function () {
      var styleMap = new OpenLayers.StyleMap({strokeWidth: 16, stokeColor: '#000000'});
      var layer = new OpenLayers.Layer.Vector('GeoJSON', {styleMap: styleMap});
      this.getMap().addLayer(layer);
      this.raiseLayer(layer);
    },
    addMapLayerToMap: function () {
      var me = this;
      return function () {
        me.raiseLayer();
      };
    },
    raiseLayer: function (geoJSONLayer) {
      var index,
        layer = null;
      if (typeof geoJSONLayer !== 'undefined') {
        layer = geoJSONLayer;
      } else {
        layer = this._map.getLayersByName('GeoJSON')[0];
      }
      index = Math.max(
        this._map.Z_INDEX_BASE.Feature,
        layer.getZIndex()
      ) + 1;
      layer.setZIndex(index);
      layer.setVisibility(true);
    },
    getState: function () {},
    getOLMapLayers: function () {
      return null;
    },
    addGeoJSON: function(data, groupId) {
      var layer = this.getMap().getLayersByName('GeoJSON')[0];
      var geoJSON = new OpenLayers.Format.GeoJSON();
      var features = geoJSON.read(data);
      var featuresWithGroupId = _.map(features, function(feature) {
        feature.attributes['groupId'] = groupId;
        return feature;
      });
      layer.addFeatures(featuresWithGroupId);
      this.raiseLayer(layer);
    },
    removeGeoJSON: function(groupId) {
      var layer = this.getMap().getLayersByName('GeoJSON')[0];
      if(layer) {
        var features = layer.getFeaturesByAttribute('groupId', groupId);
        if (features && !_.isEmpty(features)) {
          layer.removeFeatures(features);
        }
      }
    }
  }, {
    'extend': ['Oskari.mapping.mapmodule.plugin.AbstractMapModulePlugin'],
    'protocol': [
      'Oskari.mapframework.module.Module',
      'Oskari.mapframework.ui.module.common.mapmodule.Plugin'
    ]
  }
);
<commit_msg>Improve styles<commit_after>Oskari.clazz.define('Oskari.geojson.bundle.geojson.GeoJSONPlugin',
  function (conf, state) {
    var me = this;
    me._clazz = 'Oskari.geojson.bundle.geojson.GeoJSONPlugin';
    me._name = 'MarkersPlugin';
    me.state = state;
  }, {
    hasUI: function () {
      return true;
    },
    _createEventHandlers: function () {
      var me = this;

      return {
        AfterRearrangeSelectedMapLayerEvent: function (event) {
          me.raiseLayer();
        }
      };
    },
    _createRequestHandlers: function () {
      var me = this;
      return {
        'GeoJSONPlugin.AddGeoJSONRequest': Oskari.clazz.create(
          'Oskari.geojson.bundle.geojson.request.AddGeoJSONRequestHandler',
          me
        ),
        'GeoJSONPlugin.RemoveGeoJSONRequest': Oskari.clazz.create(
          'Oskari.geojson.bundle.geojson.request.RemoveGeoJSONRequestHandler',
          me
        )
      };
    },
    register: function () {
      this.getMapModule().setLayerPlugin('geojson', this);
    },
    unregister: function () {
      this.getMapModule().setLayerPlugin('geojson', null);
    },
    _startPluginImpl: function () {
      var me = this;
      me._createGeoJSONLayer();
    },
    _createGeoJSONLayer: function () {
      var styleMap = new OpenLayers.StyleMap({strokeWidth: 10, stokeColor: '#0006e8', strokeOpacity: 0.7});
      var layer = new OpenLayers.Layer.Vector('GeoJSON', {styleMap: styleMap});
      this.getMap().addLayer(layer);
      this.raiseLayer(layer);
    },
    addMapLayerToMap: function () {
      var me = this;
      return function () {
        me.raiseLayer();
      };
    },
    raiseLayer: function (geoJSONLayer) {
      var index,
        layer = null;
      if (typeof geoJSONLayer !== 'undefined') {
        layer = geoJSONLayer;
      } else {
        layer = this._map.getLayersByName('GeoJSON')[0];
      }
      index = Math.max(
        this._map.Z_INDEX_BASE.Feature,
        layer.getZIndex()
      ) + 1;
      layer.setZIndex(index);
      layer.setVisibility(true);
    },
    getState: function () {},
    getOLMapLayers: function () {
      return null;
    },
    addGeoJSON: function(data, groupId) {
      var layer = this.getMap().getLayersByName('GeoJSON')[0];
      var geoJSON = new OpenLayers.Format.GeoJSON();
      var features = geoJSON.read(data);
      var featuresWithGroupId = _.map(features, function(feature) {
        feature.attributes['groupId'] = groupId;
        return feature;
      });
      layer.addFeatures(featuresWithGroupId);
      this.raiseLayer(layer);
    },
    removeGeoJSON: function(groupId) {
      var layer = this.getMap().getLayersByName('GeoJSON')[0];
      if(layer) {
        var features = layer.getFeaturesByAttribute('groupId', groupId);
        if (features && !_.isEmpty(features)) {
          layer.removeFeatures(features);
        }
      }
    }
  }, {
    'extend': ['Oskari.mapping.mapmodule.plugin.AbstractMapModulePlugin'],
    'protocol': [
      'Oskari.mapframework.module.Module',
      'Oskari.mapframework.ui.module.common.mapmodule.Plugin'
    ]
  }
);
<|endoftext|>


<commit_before>const ServerAddr = 'ws://localhost:3000';

describe('JS-Nexus Server', function() {

  // Throw error if a Nexus server is not running
  const testConnection = new EnsureConnection();
  onDoneTesting.then(() => testConnection.close());

  setSpecHelper(NexusSpecHelpers);

  beforeEach(function() {
    if(testConnection.closed) throw new Error('no connection');

    this.hostName = 'defaultName';
    this.host;
    this.client;
  });

  when('a host and client connect', function() {
    it('can let them communicate', async function() {
      this.newHost();
      await this.onRegistered();
      expect(this.host.id).not.toEqual(undefined);
      expect(this.host.name).not.toEqual(undefined);

      this.newClient();
      await this.onServerConnect();
      let list = await this.getHostList();
      expect(list && list.length).not.toEqual(0);
      let hostRegistry = this.findHost(list, this.host.id);
      expect(hostRegistry.hostName).toBe(this.host.name);

      await this.closeHost();
      list = await this.getHostList();
      hostRegistry = this.findHost(list, this.host.id);
      expect(hostRegistry.hostName).toBe(this.host.name);
      await this.closeClient();
    });
  });
});
<commit_msg>describe the feature being tested instead of the setup<commit_after>const ServerAddr = 'ws://localhost:3000';

describe('JS-Nexus Server', function() {

  // Throw error if a Nexus server is not running
  const testConnection = new EnsureConnection();
  onDoneTesting.then(() => testConnection.close());

  setSpecHelper(NexusSpecHelpers);

  beforeEach(function() {
    if(testConnection.closed) throw new Error('no connection');

    this.hostName = 'defaultName';
    this.host;
    this.client;
  });

  describe('client.getHostList()', function() {
    it('only returns active hosts', async function() {
      this.newHost();
      await this.onRegistered();
      expect(this.host.id).not.toEqual(undefined);
      expect(this.host.name).not.toEqual(undefined);

      this.newClient();
      await this.onServerConnect();
      // Make sure the host is listed
      let list = await this.getHostList();
      expect(list && list.length).not.toEqual(0);
      let hostRegistry = this.findHost(list, this.host.id);
      expect(hostRegistry.hostName).toBe(this.host.name);

      // cleanup
      await this.closeHost();
      await this.closeClient();
    });
  });
});
<|endoftext|>


<commit_before>// prevents user typing or pasting characters that are not within a defined regex
IFS.core.preventInputRegex = (function () {
  'use strict'
  var s // private alias to settings
  return {
    settings: {
      preventInputRegexEl: 'data-prevent-input-regex'
    },
    init: function () {
      s = this.settings

      /* we catch paste/keypress commands for number fields as the input event directly clashes with browsers
        own inadequate attempts to prevent bad input */
      jQuery('[data-prevent-input-non-number]').on('paste', function (event) {
        var regex = new RegExp('[^0-9]', 'g')
        var data = event.originalEvent.clipboardData.getData('text')
        if (data.replace(regex, '') !== data) {
          event.originalEvent.preventDefault()
        }
      })

      jQuery('[data-prevent-input-non-number]').on('keypress', function (event) {
        var regex = new RegExp('[^0-9]', 'g')
        if (event.originalEvent.key.replace(regex, '') === '') {
          event.originalEvent.preventDefault()
        }
      })

      // general form that can be used with any regex
      jQuery(document).on('input', '[' + s.preventInputRegexEl + ']', function () {
        var el = jQuery(this)
        var regex = new RegExp(el.attr(s.preventInputRegexEl), 'g')
        var inputValue = el.val()
        var outputValue = inputValue.replace(regex, '')
        el.val(outputValue)
      })
    }
  }
})()
<commit_msg>IFS-3405 fix firefox keypress issue by only triggering prevent on single character input<commit_after>// prevents user typing or pasting characters that are not within a defined regex
IFS.core.preventInputRegex = (function () {
  'use strict'
  var s // private alias to settings
  return {
    settings: {
      preventInputRegexEl: 'data-prevent-input-regex'
    },
    init: function () {
      s = this.settings

      /* we catch paste/keypress commands for number fields as the input event directly clashes with browsers
        own inadequate attempts to prevent bad input */
      jQuery('[data-prevent-input-non-number]').on('paste', function (event) {
        var regex = new RegExp('[^0-9]', 'g')
        var data = event.originalEvent.clipboardData.getData('text')
        if (data.replace(regex, '') !== data) {
          event.originalEvent.preventDefault()
        }
      })

      jQuery('[data-prevent-input-non-number]').on('keypress', function (event) {
        var regex = new RegExp('[^0-9]', 'g')
        if (event.originalEvent.key.length === 1 && event.originalEvent.key.replace(regex, '') === '') {
          event.originalEvent.preventDefault()
        }
      })

      // general form that can be used with any regex
      jQuery(document).on('input', '[' + s.preventInputRegexEl + ']', function () {
        var el = jQuery(this)
        var regex = new RegExp(el.attr(s.preventInputRegexEl), 'g')
        var inputValue = el.val()
        var outputValue = inputValue.replace(regex, '')
        el.val(outputValue)
      })
    }
  }
})()
<|endoftext|>


<commit_before>'use strict';

// Server
let express = require('express');
let app = express();

app.use(express.json());
app.use(express.urlencoded());
let http = require('http').Server(app);

var livereload = require('livereload');
var reloadServer = livereload.createServer();
reloadServer.watch(__dirname);  // Enable watch on complete app folder

let Logger = require('bunyan');
let log = new Logger({
  name: 'helloapi',
  streams: [
    {
      stream: process.stdout,
      level: 'debug'
    },
    {
      path: 'logs/hello.log',
      level: 'trace'
    }
  ]
});

// Routes
let routes = require('./src/routes/index');
routes(app);

// Launch server
http.listen('3030');

// Expose
module.exports = app;
<commit_msg>Retrait de livereload (ne fonctionnant pas)<commit_after>'use strict';

// Server
let express = require('express');
let app = express();

app.use(express.json());
app.use(express.urlencoded());
let http = require('http').Server(app);

let Logger = require('bunyan');
let log = new Logger({
  name: 'helloapi',
  streams: [
    {
      stream: process.stdout,
      level: 'debug'
    },
    {
      path: 'logs/hello.log',
      level: 'trace'
    }
  ]
});

// Routes
let routes = require('./src/routes/index');
routes(app);

// Launch server
http.listen('3030');

// Expose
module.exports = app;
<|endoftext|>


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

angular.module('mm.core')

/**
 * Directive to format text rendered.
 *
 * @module mm.core
 * @ngdoc directive
 * @name mmFormatText
 * @description
 * Directive to format text rendered. Attributes it accepts:
 *     -siteid: Site ID to use.
 *     -component: The component for mmExternalContent
 *     -component-id: The component ID for mmExternalContent
 *     -after-render: Scope function to call once the content is renderered. Passes the current scope as argument.
 *     -clean: True if all HTML tags should be removed, false otherwise.
 *     -singleline: True if new lines should be removed (all the text in a single line). Only valid if clean is true.
 *     -shorten: To shorten the text. If a number is supplied, it will shorten the text to that number of characters.
 *               If a percentage is supplied the number of characters to short will be the percentage of element's width.
 *               E.g. 50% of an element with 1000px width = 500 characters.
 *               If the element has no width it'll use 100 characters. If the attribute is empty it'll use 30% width.
 *     -expand-on-click: Indicate if contents should be expanded on click (undo shorten). Only applied if "shorten" is set.
 *     -expand-title: Page title to used in expanded/fullview. Default: Description.
 *     -fullview-on-click: Indicate if should open a new state with the full contents on click. Only applied if "shorten" is set.
 *     -newlines-on-fullview: Indicate if new lines should be replaced by
 on fullview.
 *     -not-adapt-img: True if we don't want to adapt images to the screen size and add the "openfullimage" icon.
 *     -watch: True if the variable used inside the directive should be watched for changes. If the variable data is retrieved
 *             asynchronously, this value must be set to true, or the directive should be inside a ng-if, ng-repeat or similar.
 */
.directive('mmFormatText', function($interpolate, $mmText, $compile, $translate) {

    var extractVariableRegex = new RegExp('{{([^|]+)(|.*)?}}', 'i'),
        tagsToIgnore = ['AUDIO', 'VIDEO', 'BUTTON', 'INPUT', 'SELECT', 'TEXTAREA', 'A'];

    /**
     * Add mm-external-content and its extra attributes to a certain element.
     *
     * @param {Object} el            DOM element to add the attributes to.
     * @param {String} [component]   Component.
     * @param {Number} [componentId] Component ID.
     * @param {String} [siteId]      Site ID.
     */
    function addExternalContent(el, component, componentId, siteId) {
        el.setAttribute('mm-external-content', '');
        if (component) {
            el.setAttribute('component', component);
            if (componentId) {
                el.setAttribute('component-id', componentId);
            }
        }
        if (siteId) {
            el.setAttribute('siteid', siteId);
        }
    }

    /**
     * Returns the number of characters to shorten the text. If the text shouldn't be shortened, returns undefined.
     *
     * @param  {Object} element   Directive root DOM element.
     * @param  {String} [shorten] Shorten attribute. Can be undefined or a string: empty, number or a percentage.
     * @return {Number}           Number of characters to shorten the text to. Undefined if it shouldn't shorten.
     */
    function calculateShorten(element, shorten) {
        var multiplier;

        if (typeof shorten == 'string' && shorten.indexOf('%') > -1) {
            // It's a percentage. Extract the multiplier.
            multiplier = parseInt(shorten.replace(/%/g, '').trim()) / 100;
            if (isNaN(multiplier)) {
                multiplier = 0.3;
            }
        } else if (typeof shorten != 'undefined' && shorten === '') {
            // Not defined, use default value.
            multiplier = 0.3;
        } else {
            var number = parseInt(shorten);
            if (isNaN(number)) {
                return; // Return undefined so it's not shortened.
            } else {
                return number;
            }
        }

        var el = element[0],
            elWidth = el.offsetWidth || el.width || el.clientWidth;
        if (!elWidth) {
            // Cannot calculate element's width, use default value.
            return 100;
        } else {
            return Math.round(elWidth * multiplier);
        }
    }

    /**
     * Add class to adapt media to a certain element.
     *
     * @param {Object} el Dom element to add the class to.
     */
    function addMediaAdaptClass(el) {
        angular.element(el).addClass('mm-media-adapt-width');
    }

    /**
     * Format contents and render.
     *
     * @param  {Object} scope   Directive scope.
     * @param  {Object} element Directive root DOM element.
     * @param  {Object} attrs   Directive attributes.
     * @param  {String} text    Directive contents.
     * @return {Void}
     */
    function formatAndRenderContents(scope, element, attrs, text) {

        if (typeof text == 'undefined') {
            element.removeClass('hide');
            return;
        }

        attrs.shorten = calculateShorten(element, attrs.shorten);

        // If expandOnClick or fullviewOnClick are set we won't shorten the text on formatContents, we'll do it later.
        var shorten = (attrs.expandOnClick || attrs.fullviewOnClick) ? 0 : attrs.shorten;

        text = $interpolate(text)(scope); // "Evaluate" scope variables.
        text = text.trim();

        formatContents(scope, element, attrs, text, shorten).then(function(fullText) {
            if (attrs.shorten && (attrs.expandOnClick || attrs.fullviewOnClick)) {
                var shortened = $mmText.shortenText($mmText.cleanTags(fullText, false), parseInt(attrs.shorten)),
                    expanded = false;

                if (shortened.trim() === '') {
                    // The content could have images or media that were removed with shortenText. Check if that's the case.
                    var hasContent = false,
                        meaningfulTags = ['img', 'video', 'audio'];

                    angular.forEach(meaningfulTags, function(tag) {
                        if (fullText.indexOf('<'+tag) > -1) {
                            hasContent = true;
                        }
                    });

                    if (hasContent) {
                        // The content has meaningful tags. Show a placeholder to expand the content.
                        shortened = $translate.instant(attrs.expandOnClick ? 'mm.core.clicktohideshow' : 'mm.core.clicktoseefull');
                    }
                }

                element.on('click', function(e) {
                    e.preventDefault();
                    e.stopPropagation();
                    var target = e.target;
                    if (tagsToIgnore.indexOf(target.tagName) === -1 || (target.tagName === 'A' && !target.getAttribute('href'))) {
                        if (attrs.expandOnClick) {
                            // Expand/collapse.
                            expanded = !expanded;
                            element.html( expanded ? fullText : shortened);
                            if (expanded) {
                                $compile(element.contents())(scope);
                            }
                        } else {
                            // Open a new state with the interpolated contents.
                            $mmText.expandText(attrs.expandTitle || $translate.instant('mm.core.description'), text,
                                attrs.newlinesOnFullview, attrs.component, attrs.componentId);
                        }
                    }
                });

                renderText(scope, element, shortened, attrs.afterRender);
            } else {
                renderText(scope, element, fullText, attrs.afterRender);
            }
        });
    }

    /**
     * Apply formatText and set sub-directives.
     *
     * @param  {Object} scope     Directive scope.
     * @param  {Object} element   Directive root DOM element.
     * @param  {Object} attrs     Directive attributes.
     * @param  {String} text      Directive contents.
     * @param  {Number} [shorten] Number of characters to shorten contents to. If not defined, don't shorten the text.
     * @return {Promise}          Promise resolved with the formatted text.
     */
    function formatContents(scope, element, attrs, text, shorten) {

        var siteId = scope.siteid,
            component = attrs.component,
            componentId = attrs.componentId;

        // Apply format text function.
        return $mmText.formatText(text, attrs.clean, attrs.singleline, shorten).then(function(formatted) {

            var el = element[0],
                elWidth = el.offsetWidth || el.width || el.clientWidth,
                dom = angular.element('
').html(formatted); // Convert the content into DOM.

            // Walk through the content to find the links and add our directive to it.
            // Important: We need to look for links first because in 'img' we add new links without mm-link.
            angular.forEach(dom.find('a'), function(anchor) {
                anchor.setAttribute('mm-link', '');
                anchor.setAttribute('capture-link', true);
                addExternalContent(anchor, component, componentId, siteId);
            });

            // Walk through the content to find images, and add our directive.
            angular.forEach(dom.find('img'), function(img) {
                addMediaAdaptClass(img);
                addExternalContent(img, component, componentId, siteId);
                if (!attrs.notAdaptImg) {
                    // Check if image width has been adapted. If so, add an icon to view the image at full size.
                    var imgWidth = img.offsetWidth || img.width || img.clientWidth;
                    if (imgWidth > elWidth) {
                        // Wrap the image in a new div with position relative.
                        var div = angular.element('

'),
                            jqImg = angular.element(img),
                            label = $mmText.escapeHTML($translate.instant('mm.core.openfullimage')),
                            imgSrc = $mmText.escapeHTML(img.getAttribute('src'));
                        img.style.float = ''; // Disable float since image will fill the whole width.
                        jqImg.wrap(div);
                        jqImg.after('');
                    }
                }
            });

            angular.forEach(dom.find('audio'), function(el) {
                treatMedia(el, component, componentId, siteId);
            });
            angular.forEach(dom.find('video'), function(el) {
                treatVideoFilters(el);
                treatMedia(el, component, componentId, siteId);
                // Set data-tap-disabled="true" to make controls work in Android (see MOBILE-1452).
                el.setAttribute('data-tap-disabled', true);
            });
            angular.forEach(dom.find('iframe'), addMediaAdaptClass);

            // Treat selects in iOS.
            if (ionic.Platform.isIOS()) {
                angular.forEach(dom.find('select'), function(select) {
                    select.setAttribute('mm-ios-select-fix', '');
                });
            }

            // Handle buttons with inner links.
            angular.forEach(dom[0].querySelectorAll('.button'), function(button) {
                // Check if it has a link inside.
                if (button.querySelector('a')) {
                    angular.element(button).addClass('mm-button-with-inner-link');
                }
            });

            return dom.html();
        });
    }

    /**
     * Render some text on the directive's element, compile it and call afterRender.
     *
     * @param  {Object} scope         Directive scope.
     * @param  {Object} element       Directive root DOM element.
     * @param  {String} text          Directive contents.
     * @param  {String} [afterRender] Scope function to call once the content is renderered.
     * @return {Void}
     */
    function renderText(scope, element, text, afterRender) {
        element.html(text);
        element.removeClass('hide');
        $compile(element.contents())(scope);
        // Call the after render function.
        if (afterRender && scope[afterRender]) {
            scope[afterRender](scope);
        }
    }

    // Convenience function to extract YouTube Id to translate to embedded video.
    // Based on http://stackoverflow.com/questions/3452546/javascript-regex-how-to-get-youtube-video-id-from-url
    function youtubeGetId(url) {
        var regExp = /^.*youtu.be\/|v\/|\/u\/\w\/|embed\/|watch\?\??v?=?([^#\&\?]*).*/;
        var match = url.match(regExp);
        return (match && match[1].length == 11)? match[1] : false;
    }

    /**
     * Treat video filters. Currently only treating youtube video using video JS.
     *
     * @param  {Object} el           DOM element.
     */
    function treatVideoFilters(el) {
        // Treat Video JS Youtube video links and translate them to iframes.
        if (!angular.element(el).hasClass('video-js')) {
            return;
        }

        var data = JSON.parse(el.getAttribute('data-setup') || '{}'),
            youtubeId = data.techOrder && data.techOrder[0] && data.techOrder[0] == 'youtube' && data.sources && data.sources[0] &&
                data.sources[0].src && youtubeGetId(data.sources[0].src);

        if (!youtubeId) {
            return;
        }

        var iframe = document.createElement('iframe');
        iframe.id = el.id;
        iframe.src = 'https://www.youtube.com/embed/' + youtubeId;
        iframe.setAttribute('frameborder', 0);
        iframe.width = '100%';
        iframe.height = 300;

        // Replace video tag by the iframe.
        el.parentNode.insertBefore(iframe, el);
        el.parentNode.removeChild(el);
    }

    /**
     * Add media adapt class and mm-external-content to the media element and their child sources.
     *
     * @param  {Object} el           DOM element.
     * @param {String} [component]   Component.
     * @param {Number} [componentId] Component ID.
     * @param {String} [siteId]      Site ID.
     */
    function treatMedia(el, component, componentId, siteId) {
        addMediaAdaptClass(el);

        addExternalContent(el, component, componentId, siteId);
        angular.forEach(angular.element(el).find('source'), function(source) {
            source.setAttribute('target-src', source.getAttribute('src'));
            source.removeAttribute('src');
            addExternalContent(source, component, componentId, siteId);
        });
    }

    return {
        restrict: 'EA',
        scope: true,
        link: function(scope, element, attrs) {
            element.addClass('hide'); // Hide contents until they're treated.
            var content = element.html(); // Get directive's content.

            if (attrs.watch) {
                // Watch the variable inside the directive.
                var matches = content.match(extractVariableRegex);
                if (matches && typeof matches[1] == 'string') {
                    var variable = matches[1].trim();
                    scope.$watch(variable, function() {
                        formatAndRenderContents(scope, element, attrs, content);
                    });
                }
            } else {
                formatAndRenderContents(scope, element, attrs, content);
            }
        }
    };
});
<commit_msg>MOBILE-1888 text: Add youtube matching parentheses to regex<commit_after>// (C) Copyright 2015 Martin Dougiamas
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

angular.module('mm.core')

/**
 * Directive to format text rendered.
 *
 * @module mm.core
 * @ngdoc directive
 * @name mmFormatText
 * @description
 * Directive to format text rendered. Attributes it accepts:
 *     -siteid: Site ID to use.
 *     -component: The component for mmExternalContent
 *     -component-id: The component ID for mmExternalContent
 *     -after-render: Scope function to call once the content is renderered. Passes the current scope as argument.
 *     -clean: True if all HTML tags should be removed, false otherwise.
 *     -singleline: True if new lines should be removed (all the text in a single line). Only valid if clean is true.
 *     -shorten: To shorten the text. If a number is supplied, it will shorten the text to that number of characters.
 *               If a percentage is supplied the number of characters to short will be the percentage of element's width.
 *               E.g. 50% of an element with 1000px width = 500 characters.
 *               If the element has no width it'll use 100 characters. If the attribute is empty it'll use 30% width.
 *     -expand-on-click: Indicate if contents should be expanded on click (undo shorten). Only applied if "shorten" is set.
 *     -expand-title: Page title to used in expanded/fullview. Default: Description.
 *     -fullview-on-click: Indicate if should open a new state with the full contents on click. Only applied if "shorten" is set.
 *     -newlines-on-fullview: Indicate if new lines should be replaced by
 on fullview.
 *     -not-adapt-img: True if we don't want to adapt images to the screen size and add the "openfullimage" icon.
 *     -watch: True if the variable used inside the directive should be watched for changes. If the variable data is retrieved
 *             asynchronously, this value must be set to true, or the directive should be inside a ng-if, ng-repeat or similar.
 */
.directive('mmFormatText', function($interpolate, $mmText, $compile, $translate) {

    var extractVariableRegex = new RegExp('{{([^|]+)(|.*)?}}', 'i'),
        tagsToIgnore = ['AUDIO', 'VIDEO', 'BUTTON', 'INPUT', 'SELECT', 'TEXTAREA', 'A'];

    /**
     * Add mm-external-content and its extra attributes to a certain element.
     *
     * @param {Object} el            DOM element to add the attributes to.
     * @param {String} [component]   Component.
     * @param {Number} [componentId] Component ID.
     * @param {String} [siteId]      Site ID.
     */
    function addExternalContent(el, component, componentId, siteId) {
        el.setAttribute('mm-external-content', '');
        if (component) {
            el.setAttribute('component', component);
            if (componentId) {
                el.setAttribute('component-id', componentId);
            }
        }
        if (siteId) {
            el.setAttribute('siteid', siteId);
        }
    }

    /**
     * Returns the number of characters to shorten the text. If the text shouldn't be shortened, returns undefined.
     *
     * @param  {Object} element   Directive root DOM element.
     * @param  {String} [shorten] Shorten attribute. Can be undefined or a string: empty, number or a percentage.
     * @return {Number}           Number of characters to shorten the text to. Undefined if it shouldn't shorten.
     */
    function calculateShorten(element, shorten) {
        var multiplier;

        if (typeof shorten == 'string' && shorten.indexOf('%') > -1) {
            // It's a percentage. Extract the multiplier.
            multiplier = parseInt(shorten.replace(/%/g, '').trim()) / 100;
            if (isNaN(multiplier)) {
                multiplier = 0.3;
            }
        } else if (typeof shorten != 'undefined' && shorten === '') {
            // Not defined, use default value.
            multiplier = 0.3;
        } else {
            var number = parseInt(shorten);
            if (isNaN(number)) {
                return; // Return undefined so it's not shortened.
            } else {
                return number;
            }
        }

        var el = element[0],
            elWidth = el.offsetWidth || el.width || el.clientWidth;
        if (!elWidth) {
            // Cannot calculate element's width, use default value.
            return 100;
        } else {
            return Math.round(elWidth * multiplier);
        }
    }

    /**
     * Add class to adapt media to a certain element.
     *
     * @param {Object} el Dom element to add the class to.
     */
    function addMediaAdaptClass(el) {
        angular.element(el).addClass('mm-media-adapt-width');
    }

    /**
     * Format contents and render.
     *
     * @param  {Object} scope   Directive scope.
     * @param  {Object} element Directive root DOM element.
     * @param  {Object} attrs   Directive attributes.
     * @param  {String} text    Directive contents.
     * @return {Void}
     */
    function formatAndRenderContents(scope, element, attrs, text) {

        if (typeof text == 'undefined') {
            element.removeClass('hide');
            return;
        }

        attrs.shorten = calculateShorten(element, attrs.shorten);

        // If expandOnClick or fullviewOnClick are set we won't shorten the text on formatContents, we'll do it later.
        var shorten = (attrs.expandOnClick || attrs.fullviewOnClick) ? 0 : attrs.shorten;

        text = $interpolate(text)(scope); // "Evaluate" scope variables.
        text = text.trim();

        formatContents(scope, element, attrs, text, shorten).then(function(fullText) {
            if (attrs.shorten && (attrs.expandOnClick || attrs.fullviewOnClick)) {
                var shortened = $mmText.shortenText($mmText.cleanTags(fullText, false), parseInt(attrs.shorten)),
                    expanded = false;

                if (shortened.trim() === '') {
                    // The content could have images or media that were removed with shortenText. Check if that's the case.
                    var hasContent = false,
                        meaningfulTags = ['img', 'video', 'audio'];

                    angular.forEach(meaningfulTags, function(tag) {
                        if (fullText.indexOf('<'+tag) > -1) {
                            hasContent = true;
                        }
                    });

                    if (hasContent) {
                        // The content has meaningful tags. Show a placeholder to expand the content.
                        shortened = $translate.instant(attrs.expandOnClick ? 'mm.core.clicktohideshow' : 'mm.core.clicktoseefull');
                    }
                }

                element.on('click', function(e) {
                    e.preventDefault();
                    e.stopPropagation();
                    var target = e.target;
                    if (tagsToIgnore.indexOf(target.tagName) === -1 || (target.tagName === 'A' && !target.getAttribute('href'))) {
                        if (attrs.expandOnClick) {
                            // Expand/collapse.
                            expanded = !expanded;
                            element.html( expanded ? fullText : shortened);
                            if (expanded) {
                                $compile(element.contents())(scope);
                            }
                        } else {
                            // Open a new state with the interpolated contents.
                            $mmText.expandText(attrs.expandTitle || $translate.instant('mm.core.description'), text,
                                attrs.newlinesOnFullview, attrs.component, attrs.componentId);
                        }
                    }
                });

                renderText(scope, element, shortened, attrs.afterRender);
            } else {
                renderText(scope, element, fullText, attrs.afterRender);
            }
        });
    }

    /**
     * Apply formatText and set sub-directives.
     *
     * @param  {Object} scope     Directive scope.
     * @param  {Object} element   Directive root DOM element.
     * @param  {Object} attrs     Directive attributes.
     * @param  {String} text      Directive contents.
     * @param  {Number} [shorten] Number of characters to shorten contents to. If not defined, don't shorten the text.
     * @return {Promise}          Promise resolved with the formatted text.
     */
    function formatContents(scope, element, attrs, text, shorten) {

        var siteId = scope.siteid,
            component = attrs.component,
            componentId = attrs.componentId;

        // Apply format text function.
        return $mmText.formatText(text, attrs.clean, attrs.singleline, shorten).then(function(formatted) {

            var el = element[0],
                elWidth = el.offsetWidth || el.width || el.clientWidth,
                dom = angular.element('
').html(formatted); // Convert the content into DOM.

            // Walk through the content to find the links and add our directive to it.
            // Important: We need to look for links first because in 'img' we add new links without mm-link.
            angular.forEach(dom.find('a'), function(anchor) {
                anchor.setAttribute('mm-link', '');
                anchor.setAttribute('capture-link', true);
                addExternalContent(anchor, component, componentId, siteId);
            });

            // Walk through the content to find images, and add our directive.
            angular.forEach(dom.find('img'), function(img) {
                addMediaAdaptClass(img);
                addExternalContent(img, component, componentId, siteId);
                if (!attrs.notAdaptImg) {
                    // Check if image width has been adapted. If so, add an icon to view the image at full size.
                    var imgWidth = img.offsetWidth || img.width || img.clientWidth;
                    if (imgWidth > elWidth) {
                        // Wrap the image in a new div with position relative.
                        var div = angular.element('

'),
                            jqImg = angular.element(img),
                            label = $mmText.escapeHTML($translate.instant('mm.core.openfullimage')),
                            imgSrc = $mmText.escapeHTML(img.getAttribute('src'));
                        img.style.float = ''; // Disable float since image will fill the whole width.
                        jqImg.wrap(div);
                        jqImg.after('');
                    }
                }
            });

            angular.forEach(dom.find('audio'), function(el) {
                treatMedia(el, component, componentId, siteId);
            });
            angular.forEach(dom.find('video'), function(el) {
                treatVideoFilters(el);
                treatMedia(el, component, componentId, siteId);
                // Set data-tap-disabled="true" to make controls work in Android (see MOBILE-1452).
                el.setAttribute('data-tap-disabled', true);
            });
            angular.forEach(dom.find('iframe'), addMediaAdaptClass);

            // Treat selects in iOS.
            if (ionic.Platform.isIOS()) {
                angular.forEach(dom.find('select'), function(select) {
                    select.setAttribute('mm-ios-select-fix', '');
                });
            }

            // Handle buttons with inner links.
            angular.forEach(dom[0].querySelectorAll('.button'), function(button) {
                // Check if it has a link inside.
                if (button.querySelector('a')) {
                    angular.element(button).addClass('mm-button-with-inner-link');
                }
            });

            return dom.html();
        });
    }

    /**
     * Render some text on the directive's element, compile it and call afterRender.
     *
     * @param  {Object} scope         Directive scope.
     * @param  {Object} element       Directive root DOM element.
     * @param  {String} text          Directive contents.
     * @param  {String} [afterRender] Scope function to call once the content is renderered.
     * @return {Void}
     */
    function renderText(scope, element, text, afterRender) {
        element.html(text);
        element.removeClass('hide');
        $compile(element.contents())(scope);
        // Call the after render function.
        if (afterRender && scope[afterRender]) {
            scope[afterRender](scope);
        }
    }

    // Convenience function to extract YouTube Id to translate to embedded video.
    // Based on http://stackoverflow.com/questions/3452546/javascript-regex-how-to-get-youtube-video-id-from-url
    function youtubeGetId(url) {
        var regExp = /^.*(?:(?:youtu.be\/)|(?:v\/)|(?:\/u\/\w\/)|(?:embed\/)|(?:watch\?))\??v?=?([^#\&\?]*).*/;
        var match = url.match(regExp);
        return (match && match[1].length == 11)? match[1] : false;
    }

    /**
     * Treat video filters. Currently only treating youtube video using video JS.
     *
     * @param  {Object} el           DOM element.
     */
    function treatVideoFilters(el) {
        // Treat Video JS Youtube video links and translate them to iframes.
        if (!angular.element(el).hasClass('video-js')) {
            return;
        }

        var data = JSON.parse(el.getAttribute('data-setup') || '{}'),
            youtubeId = data.techOrder && data.techOrder[0] && data.techOrder[0] == 'youtube' && data.sources && data.sources[0] &&
                data.sources[0].src && youtubeGetId(data.sources[0].src);

        if (!youtubeId) {
            return;
        }

        var iframe = document.createElement('iframe');
        iframe.id = el.id;
        iframe.src = 'https://www.youtube.com/embed/' + youtubeId;
        iframe.setAttribute('frameborder', 0);
        iframe.width = '100%';
        iframe.height = 300;

        // Replace video tag by the iframe.
        el.parentNode.insertBefore(iframe, el);
        el.parentNode.removeChild(el);
    }

    /**
     * Add media adapt class and mm-external-content to the media element and their child sources.
     *
     * @param  {Object} el           DOM element.
     * @param {String} [component]   Component.
     * @param {Number} [componentId] Component ID.
     * @param {String} [siteId]      Site ID.
     */
    function treatMedia(el, component, componentId, siteId) {
        addMediaAdaptClass(el);

        addExternalContent(el, component, componentId, siteId);
        angular.forEach(angular.element(el).find('source'), function(source) {
            source.setAttribute('target-src', source.getAttribute('src'));
            source.removeAttribute('src');
            addExternalContent(source, component, componentId, siteId);
        });
    }

    return {
        restrict: 'EA',
        scope: true,
        link: function(scope, element, attrs) {
            element.addClass('hide'); // Hide contents until they're treated.
            var content = element.html(); // Get directive's content.

            if (attrs.watch) {
                // Watch the variable inside the directive.
                var matches = content.match(extractVariableRegex);
                if (matches && typeof matches[1] == 'string') {
                    var variable = matches[1].trim();
                    scope.$watch(variable, function() {
                        formatAndRenderContents(scope, element, attrs, content);
                    });
                }
            } else {
                formatAndRenderContents(scope, element, attrs, content);
            }
        }
    };
});
<|endoftext|>


<commit_before>/**
 * Javascript SwatTableView component
 *
 * @param id string Id of the matching {@link SwatTableView}.
 **/
function SwatTableView(id) {
	this.id = id;
	var self = this;
}

SwatTableView.prototype.highlightRow = function(node, highlight) {
	if (node.nodeName == 'TR') {
		var class_names = node.className.split(' ');
		if (highlight && findClass(class_names, 'odd'))
			class_names = replaceClass(class_names, 'odd', 'highlight-odd');

		else if (findClass(class_names, 'highlight-odd'))
			class_names = replaceClass(class_names, 'highlight-odd', 'odd');

		else if (highlight)
			class_names[class_names.length] = 'highlight';

		else
			class_names = replaceClass(class_names, 'highlight', '');

		node.className = classToString(class_names);

	} else if (node.parentNode)
		this.highlightRow(node.parentNode, highlight);

	function findClass(class_names, name) {
		for (var i = 0; i < class_names.length; i++)
			if (class_names[i] == name)
				return true;

		return false;
	}

	function replaceClass(class_names, from, to) {
		for (var i = 0; i < class_names.length; i++)
			if (class_names[i] == from)
				class_names[i] = to;

		return class_names;
	}

	function classToString(class_names) {
		var out = '';

		for (var i = 0; i < class_names.length; i++)
			out += class_names[i] + ' ';

		return out;
	}
}
<commit_msg>Coding conventions.<commit_after>/**
 * JavaScript for the SwatTableView widget
 *
 * @param id string Id of the matching {@link SwatTableView}.
 */
function SwatTableView(id)
{
	this.id = id;
	var self = this;
}

SwatTableView.prototype.highlightRow = function(node, highlight)
{
	if (node.nodeName == 'TR') {
		var class_names = node.className.split(' ');
		if (highlight && findClass(class_names, 'odd'))
			class_names = replaceClass(class_names, 'odd', 'highlight-odd');
		else if (findClass(class_names, 'highlight-odd'))
			class_names = replaceClass(class_names, 'highlight-odd', 'odd');
		else if (highlight)
			class_names[class_names.length] = 'highlight';
		else
			class_names = replaceClass(class_names, 'highlight', '');

		node.className = classToString(class_names);
	} else if (node.parentNode) {
		this.highlightRow(node.parentNode, highlight);
	}

	function findClass(class_names, name)
	{
		for (var i = 0; i < class_names.length; i++)
			if (class_names[i] == name)
				return true;

		return false;
	}

	function replaceClass(class_names, from, to)
	{
		for (var i = 0; i < class_names.length; i++)
			if (class_names[i] == from)
				class_names[i] = to;

		return class_names;
	}

	function classToString(class_names)
	{
		var out = '';

		for (var i = 0; i < class_names.length; i++)
			out += class_names[i] + ' ';

		return out;
	}
}
<|endoftext|>


<commit_before>import assert from 'assert';
import NeuralNetwork from '../../src/neural-network';

describe('.toFunction()', () => {
  const originalNet = new NeuralNetwork();
  const xorTrainingData = [
    {input: [0, 0], output: [0]},
    {input: [0, 1], output: [1]},
    {input: [1, 0], output: [1]},
    {input: [1, 1], output: [0]}];
  originalNet.train(xorTrainingData);
  const xor = originalNet.toFunction();
  it.only('runs same as original network', () => {
    assert.deepEqual(xor([0, 0])[0].toFixed(6), originalNet.run([0, 0])[0].toFixed(6));
    assert.deepEqual(xor([0, 1])[0].toFixed(6), originalNet.run([0, 1])[0].toFixed(6));
    assert.deepEqual(xor([1, 0])[0].toFixed(6), originalNet.run([1, 0])[0].toFixed(6));
    assert.deepEqual(xor([1, 1])[0].toFixed(6), originalNet.run([1, 1])[0].toFixed(6));
  });
});<commit_msg>remove `.only`<commit_after>import assert from 'assert';
import NeuralNetwork from '../../src/neural-network';

describe('.toFunction()', () => {
  const originalNet = new NeuralNetwork();
  const xorTrainingData = [
    {input: [0, 0], output: [0]},
    {input: [0, 1], output: [1]},
    {input: [1, 0], output: [1]},
    {input: [1, 1], output: [0]}];
  originalNet.train(xorTrainingData);
  const xor = originalNet.toFunction();
  it('runs same as original network', () => {
    assert.deepEqual(xor([0, 0])[0].toFixed(6), originalNet.run([0, 0])[0].toFixed(6));
    assert.deepEqual(xor([0, 1])[0].toFixed(6), originalNet.run([0, 1])[0].toFixed(6));
    assert.deepEqual(xor([1, 0])[0].toFixed(6), originalNet.run([1, 0])[0].toFixed(6));
    assert.deepEqual(xor([1, 1])[0].toFixed(6), originalNet.run([1, 1])[0].toFixed(6));
  });
});<|endoftext|>