<commit_before>function sleep (msec)
{
    var start = new Date().getTime();
    while (new Date().getTime() - start < msec);
}

if (Rho.System.platform == Rho.System.PLATFORM_ANDROID) {

    describe('Websocket JS API', function() {

        var srvHost = WEBSOCKET_HOST;
        var srvPort = WEBSOCKET_PORT;
        var srvURL = "ws://"+srvHost+":"+srvPort.toString();
        var ws = null;

        var receivedMessage = null;
        var connected = false;
        var cbCalled = false;

        beforeEach(function() {
            cbCalled = false;
        });

        it('Creates websocket', function() {
          ws = new Websocket(srvURL);
          expect(ws).toNotEqual(null);

          ws.onopen = function() {
            cbCalled = true;
            Rho.Log.info("Websocket connection estabilished.", "WEBSOCKET");
          };

          ws.onclose = function(event) {
            cbCalled = true;
            Rho.Log.info('Closed websocket with code: ' + event.code + ' reason: ' + event.reason, "WEBSOCKET");
          };

          ws.onmessage = function(event) {
            cbCalled = true;
            Rho.Log.info("Websocket data received " + event.data, "WEBSOCKET");
            receivedMessage = event.data;
          };

          ws.onerror = function(error) {
            cbCalled = true;
            Rho.Log.error("Error " + error.message, "WEBSOCKET");
          };

          waitsFor( function() {return cbCalled;}, 5000, "Websocket connect timeout" );
        });

        it('Connects websocket', function() {
             runs(function() { expect(ws.readyState).toEqual(Websocket.OPEN) });
        });

        it('Sends and receives message', function() {
             var message = "Test message";
             runs(function() { ws.send(message); } );
             waitsFor( function() {return cbCalled;}, 5000, "Websocket send timeout" );
             runs(function() {
                expect(receivedMessage).toEqual("Pong: " + message);
             });
        });

        it('Disconnects websocket', function() {
           runs(function() { ws.close(); });
           waitsFor( function() {return cbCalled;}, 5000, "Websocket disconnect timeout" );
           runs(function() { expect(ws.readyState).toEqual(Websocket.CLOSED) });
        });
    });

	describe("Web socket js api test by bhakta", function(){

        var srvHost = WEBSOCKET_HOST,
        srvPort = WEBSOCKET_PORT,
        srvURL = "ws://"+srvHost+":"+srvPort.toString(),

        receivedMessage = null,
        connected = false;

		it("should raise exception if url is empty", function(){
			expect(function(){new Websocket("")}).toThrow(new Error("Websocket URL is empty."));
		});

		it('Creates websocket with first argument', function(){

      var cbOnopenCalled = false;

			var ws1 = new Websocket(srvURL);
			expect(ws1).not.toEqual(null);

			ws1.onopen = function() {
				cbOnopenCalled = true;
				Rho.Log.info("Websocket connection estabilished.", "WEBSOCKET");
			};

			waitsFor( function() {return cbOnopenCalled;}, 50000, "Websocket connect timeout" );

      runs( function() { ws1.close(); } );
		});

		it('Creates websocket with second argument too', function(){

      var cbOnopenCalled = false;

			var ws2 = new Websocket(srvURL, ['soap', 'xmpp']);
			expect(ws2).not.toEqual(null);

			ws2.onopen = function() {
				cbOnopenCalled = true;
				Rho.Log.info("Websocket connection estabilished.", "WEBSOCKET");
			};

			waitsFor( function() {return cbOnopenCalled;}, 50000, "Websocket connect timeout" );

      runs( function() { ws2.close(); } );
		});

		it('should check readystate connection connecting AND should check readystate connection open and onopen callback should fire', function(){
      var cbOnopenCalled = false;
      var cbOnmessageCalled = false;
      var cbOncloseCalled = false;
      var cbOnerrorCalled = false;

			var ws3 = new Websocket(srvURL);
			expect(ws3.readyState).toEqual(Websocket.CONNECTING);
			ws3.onopen = function() {
				cbOnopenCalled = true;
				Rho.Log.info("Websocket connection estabilished.", "WEBSOCKET");
			};
			ws3.onclose = function(event) {
				cbOncloseCalled = true;
				Rho.Log.info("Websocket connection closed.", "WEBSOCKET");
			};
			ws3.onmessage = function(event) {
				cbOnmessageCalled = true;
			};
			ws3.onerror = function(error) {
				cbOnerrorCalled = true;
			};

			waitsFor(function(){
				return cbOnopenCalled;
			},50000,"Waiting connection to be opened");

			runs(function(){
				expect(ws3.readyState).toEqual(Websocket.OPEN);
				/*No other callback should get fired*/
				expect(cbOnmessageCalled).toBeFalsy();
				expect(cbOncloseCalled).toBeFalsy();
				expect(cbOnerrorCalled).toBeFalsy();
        ws3.close();
			});
		});

		it('should send message to websocket server', function(){

      var cbOnopenCalled = false;
      var cbOnmessageCalled = false;
      var cbOncloseCalled = false;
      var cbOnerrorCalled = false;

			var message = "Test message";
			var receivedMessage = "";

      var ws = new Websocket(srvURL);

			ws.onopen = function() {
				cbOnopenCalled = true;
  			Rho.Log.info("Websocket connection estabilished.", "WEBSOCKET");
      }
			ws.onclose = function(event) {
				cbOncloseCalled = true;
				Rho.Log.info("Websocket connection closed.", "WEBSOCKET");
			};
			ws.onerror = function(error) {
				cbOnerrorCalled = true;
			};
			waitsFor(function(){
				return cbOnopenCalled;
			},50000,"Waiting connection to be opened");

			ws.onmessage = function(event) {
				cbOnmessageCalled = true;
				receivedMessage = event.data;
				Rho.Log.info("Websocket data received " + event.data, "WEBSOCKET");
			};

			runs(function() {
        cbOnopenCalled = false;
				ws.send(message);
			});
			waitsFor( function() {return cbOnmessageCalled;}, 5000, "Websocket send timeout" );
			runs(function() {
				expect(receivedMessage).toEqual("Pong: " + message);
				/*No other callback should get fired*/
				expect(cbOnopenCalled).toBeFalsy();
				expect(cbOncloseCalled).toBeFalsy();
				expect(cbOnerrorCalled).toBeFalsy();

        ws.close();
			});

		});

		it('should receive JSON message from websocket server', function(){
      var cbOnopenCalled = false;
      var cbOnmessageCalled = false;
      var cbOncloseCalled = false;
      var cbOnerrorCalled = false;

      var ws = new Websocket(srvURL);

			var message = "JSON";
			var data = '';
			ws.onmessage = function(event) {
				data = JSON.parse(event.data);
				Rho.Log.info("Websocket data received " + event.data, "WEBSOCKET");
				cbOnmessageCalled = true;
			};
			ws.onopen = function() {
				cbOnopenCalled = true;
  			Rho.Log.info("Websocket connection estabilished.", "WEBSOCKET");
      };
			ws.onclose = function(event) {
				cbOncloseCalled = true;
				Rho.Log.info("Websocket connection closed.", "WEBSOCKET");
			};
			ws.onerror = function(error) {
				cbOnerrorCalled = true;
			};

			waitsFor(function(){
				return cbOnopenCalled;
			},50000,"Waiting connection to be opened");

			runs(function() {
        cbOnopenCalled = false;
				ws.send(message);
			});
			waitsFor( function() {return cbOnmessageCalled;}, 5000, "Websocket send timeout" );
			runs(function() {
				expect(data.id).toEqual("ZoomIn");
				expect(data.label).toEqual("Zoom In");
				/*No other callback should get fired*/
				expect(cbOnopenCalled).toBeFalsy();
				expect(cbOncloseCalled).toBeFalsy();
				expect(cbOnerrorCalled).toBeFalsy();
        ws.close();
			});

		});

		it('should call close() to close connection, ready state closing and then readyState as closed and onclose callback should get fire', function(){
			//If on close set here then call back is not firing.
			/*ws3.onclose = function(event) {
				alert(JSON.stringify(event));
				cbOncloseCalled = true;
				Rho.Log.info("Websocket connection closed.", "WEBSOCKET");
			};*/

      var cbOnopenCalled = false;
      var cbOnmessageCalled = false;
      var cbOncloseCalled = false;
      var cbOnerrorCalled = false;

      var ws = new Websocket(srvURL);

			ws.onopen = function() {
				cbOnopenCalled = true;
  			Rho.Log.info("Websocket connection estabilished.", "WEBSOCKET");
      }
			ws.onclose = function(event) {
				cbOncloseCalled = true;
				Rho.Log.info("Websocket connection closed.", "WEBSOCKET");
			};
			ws.onerror = function(error) {
				Rho.Log.info("Websocket error: " + error, "WEBSOCKET");
				cbOnerrorCalled = true;
			};
			ws.onmessage = function(event) {
				Rho.Log.info("Websocket data received " + event.data, "WEBSOCKET");
				cbOnmessageCalled = true;
			};

			waitsFor(function(){
				return cbOnopenCalled;
			},50000,"Waiting connection to be opened");

			runs(function(){
				Rho.Log.info("Closing websocket", "WEBSOCKET");
        cbOnopenCalled = false;
				ws.close();
			});
			waitsFor(function(){
				return cbOncloseCalled;
			},50000,"Waiting connection to be opened");
			runs(function(){
				expect(ws.readyState).toEqual(Websocket.CLOSED);
				/*No other callback should get fired*/
				expect(cbOnopenCalled).toBeFalsy();
				expect(cbOnmessageCalled).toBeFalsy();
				expect(cbOnerrorCalled).toBeFalsy();
			});
		});

		xit('should not crash if send called after close', function(){
			var message = "Test message";
			var receivedMessage = "";

      var cbOnopenCalled = false;
      var cbOnmessageCalled = false;
      var cbOncloseCalled = false;
      var cbOnerrorCalled = false;

      var ws = new Websocket(srvURL);

			ws.onopen = function() {
				cbOnopenCalled = true;
  			Rho.Log.info("Websocket connection estabilished.", "WEBSOCKET");
      }
			ws.onclose = function(event) {
				cbOncloseCalled = true;
				Rho.Log.info("Websocket connection closed.", "WEBSOCKET");
			};
			ws.onerror = function(error) {
				cbOnerrorCalled = true;
			};
			ws.onmessage = function(event) {
				Rho.Log.info("Websocket data received " + event.data, "WEBSOCKET");
        receivedMessage = event.data;
				cbOnmessageCalled = true;
			};

			waitsFor(function(){
				return cbOnopenCalled;
			},50000,"Waiting connection to be opened");

      runs(function(){
        cbOnopenCalled = false;
				ws.close();
			});

			waitsFor(function(){
				return cbOncloseCalled;
			},50000,"Waiting connection to be opened");

			runs(function() {
				ws.send(message);
			});

			waitsFor( function() {return cbOnerrorCalled;}, 5000, "Websocket send timeout" );

			runs(function() {
				expect(receivedMessage).not.toEqual("Pong: " + message);
			});
		});

		it("should check onclose callback should get fired after onerror callback when web socket not able to connect to socket server",function(){

      var cbOncloseCalled = false;
      var cbOnerrorCalled = false;

			var ws3 = new Websocket("ws://some.unavailable.domain");

			ws3.onclose = function(event) {
				Rho.Log.info("Websocket onclose", "WEBSOCKET");
				cbOncloseCalled = true;
			};
			ws3.onerror = function(error) {
				Rho.Log.info("Websocket onerror " + error, "WEBSOCKET");
				cbOnerrorCalled = true;
			};

			waitsFor(function(){
				return cbOncloseCalled && cbOnerrorCalled;
			},80000,"Waiting onerror followed by on close callback should fire");

		});

	});
}<commit_msg>enables crashing test in websocket.<commit_after>function sleep (msec)
{
    var start = new Date().getTime();
    while (new Date().getTime() - start < msec);
}

if (Rho.System.platform == Rho.System.PLATFORM_ANDROID) {

    describe('Websocket JS API', function() {

        var srvHost = WEBSOCKET_HOST;
        var srvPort = WEBSOCKET_PORT;
        var srvURL = "ws://"+srvHost+":"+srvPort.toString();
        var ws = null;

        var receivedMessage = null;
        var connected = false;
        var cbCalled = false;

        beforeEach(function() {
            cbCalled = false;
        });

        it('Creates websocket', function() {
          ws = new Websocket(srvURL);
          expect(ws).toNotEqual(null);

          ws.onopen = function() {
            cbCalled = true;
            Rho.Log.info("Websocket connection estabilished.", "WEBSOCKET");
          };

          ws.onclose = function(event) {
            cbCalled = true;
            Rho.Log.info('Closed websocket with code: ' + event.code + ' reason: ' + event.reason, "WEBSOCKET");
          };

          ws.onmessage = function(event) {
            cbCalled = true;
            Rho.Log.info("Websocket data received " + event.data, "WEBSOCKET");
            receivedMessage = event.data;
          };

          ws.onerror = function(error) {
            cbCalled = true;
            Rho.Log.error("Error " + error.message, "WEBSOCKET");
          };

          waitsFor( function() {return cbCalled;}, 5000, "Websocket connect timeout" );
        });

        it('Connects websocket', function() {
             runs(function() { expect(ws.readyState).toEqual(Websocket.OPEN) });
        });

        it('Sends and receives message', function() {
             var message = "Test message";
             runs(function() { ws.send(message); } );
             waitsFor( function() {return cbCalled;}, 5000, "Websocket send timeout" );
             runs(function() {
                expect(receivedMessage).toEqual("Pong: " + message);
             });
        });

        it('Disconnects websocket', function() {
           runs(function() { ws.close(); });
           waitsFor( function() {return cbCalled;}, 5000, "Websocket disconnect timeout" );
           runs(function() { expect(ws.readyState).toEqual(Websocket.CLOSED) });
        });
    });

	describe("Web socket js api test by bhakta", function(){

        var srvHost = WEBSOCKET_HOST,
        srvPort = WEBSOCKET_PORT,
        srvURL = "ws://"+srvHost+":"+srvPort.toString(),

        receivedMessage = null,
        connected = false;

		it("should raise exception if url is empty", function(){
			expect(function(){new Websocket("")}).toThrow(new Error("Websocket URL is empty."));
		});

		it('Creates websocket with first argument', function(){

			var cbOnopenCalled = false;

			var ws1 = new Websocket(srvURL);
			expect(ws1).not.toEqual(null);

			ws1.onopen = function() {
				cbOnopenCalled = true;
				Rho.Log.info("Websocket connection estabilished.", "WEBSOCKET");
			};

			waitsFor( function() {return cbOnopenCalled;}, 50000, "Websocket connect timeout" );

			runs( function() { ws1.close(); } );
		});

		it('Creates websocket with second argument too', function(){

			var cbOnopenCalled = false;

			var ws2 = new Websocket(srvURL, ['soap', 'xmpp']);
			expect(ws2).not.toEqual(null);

			ws2.onopen = function() {
				cbOnopenCalled = true;
				Rho.Log.info("Websocket connection estabilished.", "WEBSOCKET");
			};

			waitsFor( function() {return cbOnopenCalled;}, 50000, "Websocket connect timeout" );

			runs( function() { ws2.close(); } );
		});

		it('should check readystate connection connecting AND should check readystate connection open and onopen callback should fire', function(){
			var cbOnopenCalled = false;
			var cbOnmessageCalled = false;
			var cbOncloseCalled = false;
			var cbOnerrorCalled = false;

			var ws3 = new Websocket(srvURL);
			expect(ws3.readyState).toEqual(Websocket.CONNECTING);
			ws3.onopen = function() {
				cbOnopenCalled = true;
				Rho.Log.info("Websocket connection estabilished.", "WEBSOCKET");
			};
			ws3.onclose = function(event) {
				cbOncloseCalled = true;
				Rho.Log.info("Websocket connection closed.", "WEBSOCKET");
			};
			ws3.onmessage = function(event) {
				cbOnmessageCalled = true;
			};
			ws3.onerror = function(error) {
				cbOnerrorCalled = true;
			};

			waitsFor(function(){
				return cbOnopenCalled;
			},50000,"Waiting connection to be opened");

			runs(function(){
				expect(ws3.readyState).toEqual(Websocket.OPEN);
				/*No other callback should get fired*/
				expect(cbOnmessageCalled).toBeFalsy();
				expect(cbOncloseCalled).toBeFalsy();
				expect(cbOnerrorCalled).toBeFalsy();
				ws3.close();
			});
		});

		it('should send message to websocket server', function(){

			var cbOnopenCalled = false;
			var cbOnmessageCalled = false;
			var cbOncloseCalled = false;
			var cbOnerrorCalled = false;

			var message = "Test message";
			var receivedMessage = "";

			var ws = new Websocket(srvURL);

			ws.onopen = function() {
				cbOnopenCalled = true;
				Rho.Log.info("Websocket connection estabilished.", "WEBSOCKET");
			}
			ws.onclose = function(event) {
				cbOncloseCalled = true;
				Rho.Log.info("Websocket connection closed.", "WEBSOCKET");
			};
			ws.onerror = function(error) {
				cbOnerrorCalled = true;
			};
			waitsFor(function(){
				return cbOnopenCalled;
			},50000,"Waiting connection to be opened");

			ws.onmessage = function(event) {
				cbOnmessageCalled = true;
				receivedMessage = event.data;
				Rho.Log.info("Websocket data received " + event.data, "WEBSOCKET");
			};

			runs(function() {
				cbOnopenCalled = false;
				ws.send(message);
			});
			waitsFor( function() {return cbOnmessageCalled;}, 5000, "Websocket send timeout" );
			runs(function() {
				expect(receivedMessage).toEqual("Pong: " + message);
				/*No other callback should get fired*/
				expect(cbOnopenCalled).toBeFalsy();
				expect(cbOncloseCalled).toBeFalsy();
				expect(cbOnerrorCalled).toBeFalsy();

				ws.close();
			});

		});

		it('should receive JSON message from websocket server', function(){
			var cbOnopenCalled = false;
			var cbOnmessageCalled = false;
			var cbOncloseCalled = false;
			var cbOnerrorCalled = false;

			var ws = new Websocket(srvURL);

			var message = "JSON";
			var data = '';
			ws.onmessage = function(event) {
				data = JSON.parse(event.data);
				Rho.Log.info("Websocket data received " + event.data, "WEBSOCKET");
				cbOnmessageCalled = true;
			};
			ws.onopen = function() {
				cbOnopenCalled = true;
				Rho.Log.info("Websocket connection estabilished.", "WEBSOCKET");
			};
			ws.onclose = function(event) {
				cbOncloseCalled = true;
				Rho.Log.info("Websocket connection closed.", "WEBSOCKET");
			};
			ws.onerror = function(error) {
				cbOnerrorCalled = true;
			};

			waitsFor(function(){
				return cbOnopenCalled;
			},50000,"Waiting connection to be opened");

			runs(function() {
				cbOnopenCalled = false;
				ws.send(message);
			});
			waitsFor( function() {return cbOnmessageCalled;}, 5000, "Websocket send timeout" );
			runs(function() {
				expect(data.id).toEqual("ZoomIn");
				expect(data.label).toEqual("Zoom In");
				/*No other callback should get fired*/
				expect(cbOnopenCalled).toBeFalsy();
				expect(cbOncloseCalled).toBeFalsy();
				expect(cbOnerrorCalled).toBeFalsy();
				ws.close();
			});

		});

		it('should call close() to close connection, ready state closing and then readyState as closed and onclose callback should get fire', function(){
			//If on close set here then call back is not firing.
			/*ws3.onclose = function(event) {
				alert(JSON.stringify(event));
				cbOncloseCalled = true;
				Rho.Log.info("Websocket connection closed.", "WEBSOCKET");
			};*/

			var cbOnopenCalled = false;
			var cbOnmessageCalled = false;
			var cbOncloseCalled = false;
			var cbOnerrorCalled = false;

			var ws = new Websocket(srvURL);

			ws.onopen = function() {
				cbOnopenCalled = true;
  			Rho.Log.info("Websocket connection estabilished.", "WEBSOCKET");
      }
			ws.onclose = function(event) {
				cbOncloseCalled = true;
				Rho.Log.info("Websocket connection closed.", "WEBSOCKET");
			};
			ws.onerror = function(error) {
				Rho.Log.info("Websocket error: " + error, "WEBSOCKET");
				cbOnerrorCalled = true;
			};
			ws.onmessage = function(event) {
				Rho.Log.info("Websocket data received " + event.data, "WEBSOCKET");
				cbOnmessageCalled = true;
			};

			waitsFor(function(){
				return cbOnopenCalled;
			},50000,"Waiting connection to be opened");

			runs(function(){
				Rho.Log.info("Closing websocket", "WEBSOCKET");
				cbOnopenCalled = false;
				ws.close();
			});
			waitsFor(function(){
				return cbOncloseCalled;
			},50000,"Waiting connection to be opened");
			runs(function(){
				expect(ws.readyState).toEqual(Websocket.CLOSED);
				/*No other callback should get fired*/
				expect(cbOnopenCalled).toBeFalsy();
				expect(cbOnmessageCalled).toBeFalsy();
				expect(cbOnerrorCalled).toBeFalsy();
			});
		});

		it('should not crash if send called after close', function(){
			var message = "Test message";
			var receivedMessage = "";

			var cbOnopenCalled = false;
			var cbOnmessageCalled = false;
			var cbOncloseCalled = false;
			var cbOnerrorCalled = false;

			var ws = new Websocket(srvURL);

			ws.onopen = function() {
				cbOnopenCalled = true;
				Rho.Log.info("Websocket connection estabilished.", "WEBSOCKET");
			};
			ws.onclose = function(event) {
				cbOncloseCalled = true;
				Rho.Log.info("Websocket connection closed.", "WEBSOCKET");
			};
			ws.onerror = function(error) {
				cbOnerrorCalled = true;
			};
			ws.onmessage = function(event) {
				Rho.Log.info("Websocket data received " + event.data, "WEBSOCKET");
				receivedMessage = event.data;
				cbOnmessageCalled = true;
			};

			waitsFor(function(){
				return cbOnopenCalled;
			},50000,"Waiting connection to be opened");

			runs(function(){
				cbOnopenCalled = false;
				ws.close();
			});

			waitsFor(function(){
				return cbOncloseCalled;
			},50000,"Waiting connection to be opened");

			runs(function() {
				ws.send(message);
			});

			waitsFor( function() {return cbOnerrorCalled;}, 5000, "Websocket send timeout" );

			runs(function() {
				expect(receivedMessage).not.toEqual("Pong: " + message);
			});
		});

		it("should check onclose callback should get fired after onerror callback when web socket not able to connect to socket server",function(){

			var cbOncloseCalled = false;
			var cbOnerrorCalled = false;

			var ws3 = new Websocket("ws://some.unavailable.domain");

			ws3.onclose = function(event) {
				Rho.Log.info("Websocket onclose", "WEBSOCKET");
				cbOncloseCalled = true;
			};
			ws3.onerror = function(error) {
				Rho.Log.info("Websocket onerror " + error, "WEBSOCKET");
				cbOnerrorCalled = true;
			};

			waitsFor(function(){
				return cbOncloseCalled && cbOnerrorCalled;
			},80000,"Waiting onerror followed by on close callback should fire");

		});

	});
}<|endoftext|>


<commit_before>import SimpleSchema from 'simpl-schema';

const usersEnrollSchema = new SimpleSchema({
  subject: {
    type: String,
    // suggested maximum length of email subject
    // https://stackoverflow.com/a/1592310/1191545
    max: 78
  },
  message: {
    type: String,
    optional: false
  },
  emailAddresses: {
    type: Array,
    regEx: SimpleSchema.RegEx.Email,
    optional: false
  }
});

export default usersEnrollSchema;
<commit_msg>Add data type and validation regEx for emailAddresses array item(s)<commit_after>import SimpleSchema from 'simpl-schema';

const usersEnrollSchema = new SimpleSchema({
  subject: {
    type: String,
    // suggested maximum length of email subject
    // https://stackoverflow.com/a/1592310/1191545
    max: 78
  },
  message: {
    type: String,
    optional: false
  },
  emailAddresses: {
    type: Array,
    optional: false
  },
  'emailAddresses.$': {
    type: String,
    regEx: SimpleSchema.RegEx.Email,
  }
});

export default usersEnrollSchema;
<|endoftext|>


<commit_before>import styled from 'styled-components';
import ContentBlock from 'components/ContentBlock';

export default styled(ContentBlock) `
  font-size: ${p=>p.theme.isArabic?'14px':'16px'};
  line-height: ${p=>p.theme.isArabic?'23px':'22px'};
  font-weight: bold;
  color: white;
  max-width: 480px;
  display: inline-block;
  margin-top: 36px;

  p {
    font-weight: normal;
    font-family: Avenir Heavy;
    letter-spacing: 0.5px;
  }
`
<commit_msg>Avoid the changes to arabic<commit_after>import styled from 'styled-components';
import ContentBlock from 'components/ContentBlock';

export default styled(ContentBlock) `
  font-size: ${p=>p.theme.isArabic?'14px':'16px'};
  line-height: ${p=>p.theme.isArabic?'23px':'22px'};
  font-weight: bold;
  color: white;
  max-width: 480px;
  display: inline-block;
  margin-top: 36px;

  ${p=>p.theme.isArabic ?
      "" :
      `
      p {
        font-weight: normal;
        font-family: Avenir Heavy;
        letter-spacing: 0.5px;
      }
      `
  };

`
<|endoftext|>


<commit_before>/*******************************************************************************
 * Copyright 2015 France Labs
 *
 * 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.
 ******************************************************************************/
AjaxFranceLabs.QueryHighlightingWidget = AjaxFranceLabs.FacetCore.extend({

  // Variables
  elm : null,
  name : null,
  highlightedElms : {},
  currentHighlight : {
    word : "",
    current : 0
  },

  // Methods

  initContent : function(widgetContentDiv) {

  },

  refreshCurrentHighlight : function(docContentDiv) {
    var hlWord = this.currentHighlight.word;
    var current = this.currentHighlight.current;
    if (hlWord != "" && current != -1) {
      var hlElement = this.highlightedElms[hlWord].elements[current];
      docContentDiv.find(".current-hlmark").removeClass("current-hlmark").addClass("hlmark");
      hlElement.removeClass("hlmark");
      hlElement.addClass("current-hlmark");
    }
  },

  // Construct a list of jquery elements for each highlighted word
  updateHlElmsMaps : function(docContentDiv) {
    var self = this;
    for ( var hlWord in this.highlightedElms) {
      // First of all, (re)initialize the list of jquery elements for the
      // current hlWord
      this.highlightedElms[hlWord].elements = [];
      var cpt = 0;

      // Search highlighted elements and if they correspond to the current
      // hlWord then put them in the list
      docContentDiv.find(".hlmark").each(function() {
        if ($(this).html().toLowerCase() == hlWord) {
          self.highlightedElms[hlWord].elements[cpt] = $(this);
          cpt++;
        }
      });
    }
  },

  applyHighlights : function(text, hlWord) {
    var regex = new RegExp('\\b' + hlWord + '\\b', "ig");
    text = text.replace(regex, '$&');

    // Init jquery elements map for current hlWord
    this.highlightedElms[hlWord] = {
      elements : [],
      current : -1
    };

    // Clean current highlight if any
    text = text.replace("", "");

    return text;
  },

  removeHighlights : function(text, hlWord) {

    // Clean current highlight if any
    var currentHlRegex = new RegExp("\(" + hlWord + "\)", "ig");

    // If the regex match this means that there is a current highlight on the
    // hlWord so it needs to be removed
    if (text.match(currentHlRegex)) {
      text = text.replace(currentHlRegex, '$1');
      // Reset currentHighlight
      this.currentHighlight.word = "";
      this.currentHighlight.current = 0;
    }

    // Clean standard highlights
    var regex = new RegExp('\(' + hlWord + '\)', "ig");
    text = text.replace(/\n$/g, '\n\n').replace(regex, '$1');

    // Remove indexes map
    delete this.highlightedElms[hlWord];

    // Clean current highlight if any (in case the current highlight is another
    // word)
    text = text.replace("", "");

    return text;
  },

  // Adjust the scroll position of the docContentDiv to be sure that the
  // hlElement is visible
  adjustScroll : function(hlElement, docContentDiv) {
    var lineHeight = parseInt(docContentDiv.css("line-height").replace("px", ""));
    // Offset of the docContentDiv
    var contentDivOffset = docContentDiv.offset().top;
    // Position of the hlElement, relative to the docContentDiv offset
    var hlPos = hlElement.position().top;
    // Current scrollTop
    var scrollTop = docContentDiv.scrollTop();
    // Current scrollBottom
    var scrollBottom = scrollTop + docContentDiv.height();
    // Ideal scrollTop to be sure that the hlElement is visible
    var targetScrollTop = scrollTop + hlPos - contentDivOffset - lineHeight;
    if (targetScrollTop < 0) {
      targetScrollTop = 0;
    }

    // If the targetScrollTop is out of the current limits then force the
    // scrollTop to the targetScrollTop
    if (targetScrollTop < scrollTop || targetScrollTop > scrollBottom) {
      docContentDiv.scrollTop(targetScrollTop);
    }
  },

  updateWidgetContent : function(docContentDiv, widgetDiv, widgetContentDiv, docId, docPos, params, data, qId) {
    var self = this;
    widgetContentDiv.html("");
    if (data != undefined && data != null) {
      // Retrieve the query highlighting if any
      var highlighting = data.highlighting;
      var termsCollection = [];
      // Fill the termsCollection with the terms found in the query highlighting
      for ( var file in highlighting) {
        var fileHighlighting = highlighting[file];
        for ( var highlightField in fileHighlighting) {
          if (highlightField.indexOf("content") != -1 || highlightField == "exactContent") {
            if (fileHighlighting[highlightField].length > 0) {
              var contentHighlight = fileHighlighting[highlightField][0];
              var termRegex = /(.*?)<\/span>*/gm;
              var match = termRegex.exec(contentHighlight);
              while (match != null) {
                var foundTerm = match[1].trim().toLowerCase();
                if (termsCollection.indexOf(foundTerm) == -1) {
                  termsCollection.push(foundTerm);
                }
                match = termRegex.exec(contentHighlight);
              }
            }
          }
        }
      }

      // For each term, create a term button
      if (termsCollection.length > 0) {
        for (var i = 0; i < termsCollection.length; i++) {
          var divTerm = $("
 "
              + termsCollection[i] + "
");
          widgetContentDiv.append(divTerm);
        }

        $(".highlight-term-value").click(function() {
          // Word to highlight
          var hlWord = $(this).html();
          // Text of the docContentDiv
          var text = docContentDiv.html();
          if ($(this).hasClass("hlmark")) {
            // The hlWord is already highlighted so "un-highlight" it
            $(this).removeClass("hlmark");
            text = self.removeHighlights(text, hlWord);
            docContentDiv.html(text);
            // docContentDiv has been updated so we need to update the
            // highlighted jquery elements list for each highlighted word
            self.updateHlElmsMaps(docContentDiv);
            // Re set the current highlight in case it was on an other word
            self.refreshCurrentHighlight(docContentDiv);

            // Hide the previous and next buttons
            var nextButton = $(this).parent().children(".highlight-term-next");
            var prevButton = $(this).parent().children(".highlight-term-prev");
            nextButton.css("visibility", "hidden");
            prevButton.css("visibility", "hidden");
          } else {
            // The hlWord is not highlighted so highlight it
            $(this).addClass("hlmark");
            var highlightedText = self.applyHighlights(text, hlWord);
            docContentDiv.html(highlightedText);
            // docContentDiv has been updated so we need to update the
            // highlighted jquery elements list for each highlighted word
            self.updateHlElmsMaps(docContentDiv);
            // Re set the current highlight in case it was on an other word
            self.refreshCurrentHighlight(docContentDiv);

            // Display the previous and next buttons
            var nextButton = $(this).parent().children(".highlight-term-next");
            var prevButton = $(this).parent().children(".highlight-term-prev");
            nextButton.css("visibility", "visible");
            prevButton.css("visibility", "visible");
          }
        });

        $(".highlight-term-next").click(function() {
          var termVal = $(this).parent().children(".highlight-term-value");
          var hlWord = termVal.html();
          // reset others words current
          for ( var word in self.highlightedElms) {
            if (word != hlWord) {
              self.highlightedElms[word].current = -1;
            }
          }

          // Calculate the current index of the jquery element to highlight
          var current = self.highlightedElms[hlWord].current;
          if (self.highlightedElms[hlWord].elements[current + 1] != undefined) {
            current++;
          } else {
            current = 0;
          }
          self.highlightedElms[hlWord].current = current;

          // refresh the current highlight and adjust scroll
          var hlElement = self.highlightedElms[hlWord].elements[current];
          self.currentHighlight.word = hlWord;
          self.currentHighlight.current = current;
          self.refreshCurrentHighlight(docContentDiv);
          self.adjustScroll(hlElement, docContentDiv);
        });

        $(".highlight-term-prev").click(function() {
          var termVal = $(this).parent().children(".highlight-term-value");
          var hlWord = termVal.html();

          // reset others words current
          for ( var word in self.highlightedElms) {
            if (word != hlWord) {
              self.highlightedElms[word].current = -1;
            }
          }

          // Calculate the current index of the jquery element to highlight
          var current = self.highlightedElms[hlWord].current;
          if (self.highlightedElms[hlWord].elements[current - 1] != undefined) {
            current--;
          } else {
            current = self.highlightedElms[hlWord].elements.length - 1;
          }
          self.highlightedElms[hlWord].current = current;

          // refresh the current highlight and adjust scroll
          var hlElement = self.highlightedElms[hlWord].elements[current];
          self.currentHighlight.word = hlWord;
          self.currentHighlight.current = current;
          self.refreshCurrentHighlight(docContentDiv);
          self.adjustScroll(hlElement, docContentDiv);
        });
      } else {
        widgetDiv.hide();
      }
    } else {
      widgetDiv.hide();
    }
  }
});
<commit_msg>Bugfix Preview Highlight<commit_after>/*******************************************************************************
 * Copyright 2015 France Labs
 *
 * 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.
 ******************************************************************************/
AjaxFranceLabs.QueryHighlightingWidget = AjaxFranceLabs.FacetCore.extend({

  // Variables
  elm : null,
  name : null,
  highlightedElms : {},
  currentHighlight : {
    word : "",
    current : 0
  },

  // Methods

  initContent : function(widgetContentDiv) {

  },

  refreshCurrentHighlight : function(docContentDiv) {
    var hlWord = this.currentHighlight.word;
    var current = this.currentHighlight.current;
    if (hlWord != "" && current != -1) {
      var hlElement = this.highlightedElms[hlWord].elements[current];
      docContentDiv.find(".current-hlmark").removeClass("current-hlmark").addClass("hlmark");
      hlElement.removeClass("hlmark");
      hlElement.addClass("current-hlmark");
    }
  },

  // Construct a list of jquery elements for each highlighted word
  updateHlElmsMaps : function(docContentDiv) {
    var self = this;
    for ( var hlWord in this.highlightedElms) {
      // First of all, (re)initialize the list of jquery elements for the
      // current hlWord
      this.highlightedElms[hlWord].elements = [];
      var cpt = 0;

      // Search highlighted elements and if they correspond to the current
      // hlWord then put them in the list
      docContentDiv.find(".hlmark").each(function() {
        if ($(this).html().toLowerCase() == hlWord) {
          self.highlightedElms[hlWord].elements[cpt] = $(this);
          cpt++;
        }
      });
    }
  },

  applyHighlights : function(text, hlWord) {
    var regex = new RegExp('\\b' + hlWord + '\\b', "ig");
    text = text.replace(regex, '$&');

    // Init jquery elements map for current hlWord
    this.highlightedElms[hlWord] = {
      elements : [],
      current : -1
    };

    // Clean current highlight if any
    text = text.replace("", "");

    return text;
  },

  removeHighlights : function(text, hlWord) {

    // Clean current highlight if any
    var currentHlRegex = new RegExp("\(" + hlWord + "\)", "ig");

    // If the regex match this means that there is a current highlight on the
    // hlWord so it needs to be removed
    if (text.match(currentHlRegex)) {
      text = text.replace(currentHlRegex, '$1');
      // Reset currentHighlight
      this.currentHighlight.word = "";
      this.currentHighlight.current = 0;
    }

    // Clean standard highlights
    var regex = new RegExp('\(' + hlWord + '\)', "ig");
    text = text.replace(/\n$/g, '\n\n').replace(regex, '$1');

    // Remove indexes map
    delete this.highlightedElms[hlWord];

    // Clean current highlight if any (in case the current highlight is another
    // word)
    text = text.replace("", "");

    return text;
  },

  // Adjust the scroll position of the docContentDiv to be sure that the
  // hlElement is visible
  adjustScroll : function(hlElement, docContentDiv) {
    var lineHeight = parseInt(docContentDiv.css("line-height").replace("px", ""));
    // Offset of the docContentDiv
    var contentDivOffset = docContentDiv.offset().top;
    // Position of the hlElement, relative to the docContentDiv offset
    var hlPos = hlElement.position().top;
    // Current scrollTop
    var scrollTop = docContentDiv.scrollTop();
    // Current scrollBottom
    var scrollBottom = scrollTop + docContentDiv.height();
    // Ideal scrollTop to be sure that the hlElement is visible
    var targetScrollTop = scrollTop + hlPos - contentDivOffset - lineHeight;
    if (targetScrollTop < 0) {
      targetScrollTop = 0;
    }

    // If the targetScrollTop is out of the current limits then force the
    // scrollTop to the targetScrollTop
    if (targetScrollTop < scrollTop || targetScrollTop > scrollBottom) {
      docContentDiv.scrollTop(targetScrollTop);
    }
  },

  updateWidgetContent : function(docContentDiv, widgetDiv, widgetContentDiv, docId, docPos, params, data, qId) {
    var self = this;
    widgetContentDiv.html("");
    if (data != undefined && data != null) {
      // Retrieve the query highlighting if any
      var fileHighlighting = data.highlighting[docId];
      var termsCollection = [];
      // Fill the termsCollection with the terms found in the query highlighting
      for ( var highlightField in fileHighlighting) {
        if (highlightField.indexOf("content") != -1 || highlightField == "exactContent") {
          if (fileHighlighting[highlightField].length > 0) {
            var contentHighlight = fileHighlighting[highlightField][0];
            var termRegex = /(.*?)<\/span>*/gm;
            var match = termRegex.exec(contentHighlight);
            while (match != null) {
              var foundTerm = match[1].trim().toLowerCase();
              if (termsCollection.indexOf(foundTerm) == -1) {
                termsCollection.push(foundTerm);
              }
              match = termRegex.exec(contentHighlight);
            }
          }
        }
      }

      // For each term, create a term button
      if (termsCollection.length > 0) {
        for (var i = 0; i < termsCollection.length; i++) {
          var divTerm = $("
 "
              + termsCollection[i] + "
");
          widgetContentDiv.append(divTerm);
        }

        $(".highlight-term-value").click(function() {
          // Word to highlight
          var hlWord = $(this).html();
          // Text of the docContentDiv
          var text = docContentDiv.html();
          if ($(this).hasClass("hlmark")) {
            // The hlWord is already highlighted so "un-highlight" it
            $(this).removeClass("hlmark");
            text = self.removeHighlights(text, hlWord);
            docContentDiv.html(text);
            // docContentDiv has been updated so we need to update the
            // highlighted jquery elements list for each highlighted word
            self.updateHlElmsMaps(docContentDiv);
            // Re set the current highlight in case it was on an other word
            self.refreshCurrentHighlight(docContentDiv);

            // Hide the previous and next buttons
            var nextButton = $(this).parent().children(".highlight-term-next");
            var prevButton = $(this).parent().children(".highlight-term-prev");
            nextButton.css("visibility", "hidden");
            prevButton.css("visibility", "hidden");
          } else {
            // The hlWord is not highlighted so highlight it
            $(this).addClass("hlmark");
            var highlightedText = self.applyHighlights(text, hlWord);
            docContentDiv.html(highlightedText);
            // docContentDiv has been updated so we need to update the
            // highlighted jquery elements list for each highlighted word
            self.updateHlElmsMaps(docContentDiv);
            // Re set the current highlight in case it was on an other word
            self.refreshCurrentHighlight(docContentDiv);

            // Display the previous and next buttons
            var nextButton = $(this).parent().children(".highlight-term-next");
            var prevButton = $(this).parent().children(".highlight-term-prev");
            nextButton.css("visibility", "visible");
            prevButton.css("visibility", "visible");
          }
        });

        $(".highlight-term-next").click(function() {
          var termVal = $(this).parent().children(".highlight-term-value");
          var hlWord = termVal.html();
          // reset others words current
          for ( var word in self.highlightedElms) {
            if (word != hlWord) {
              self.highlightedElms[word].current = -1;
            }
          }

          // Calculate the current index of the jquery element to highlight
          var current = self.highlightedElms[hlWord].current;
          if (self.highlightedElms[hlWord].elements[current + 1] != undefined) {
            current++;
          } else {
            current = 0;
          }
          self.highlightedElms[hlWord].current = current;

          // refresh the current highlight and adjust scroll
          var hlElement = self.highlightedElms[hlWord].elements[current];
          self.currentHighlight.word = hlWord;
          self.currentHighlight.current = current;
          self.refreshCurrentHighlight(docContentDiv);
          self.adjustScroll(hlElement, docContentDiv);
        });

        $(".highlight-term-prev").click(function() {
          var termVal = $(this).parent().children(".highlight-term-value");
          var hlWord = termVal.html();

          // reset others words current
          for ( var word in self.highlightedElms) {
            if (word != hlWord) {
              self.highlightedElms[word].current = -1;
            }
          }

          // Calculate the current index of the jquery element to highlight
          var current = self.highlightedElms[hlWord].current;
          if (self.highlightedElms[hlWord].elements[current - 1] != undefined) {
            current--;
          } else {
            current = self.highlightedElms[hlWord].elements.length - 1;
          }
          self.highlightedElms[hlWord].current = current;

          // refresh the current highlight and adjust scroll
          var hlElement = self.highlightedElms[hlWord].elements[current];
          self.currentHighlight.word = hlWord;
          self.currentHighlight.current = current;
          self.refreshCurrentHighlight(docContentDiv);
          self.adjustScroll(hlElement, docContentDiv);
        });
      } else {
        widgetDiv.hide();
      }
    } else {
      widgetDiv.hide();
    }
  }
});
<|endoftext|>


<commit_before>'use strict';

const fs = require('original-fs');
const co = require('co');
const lo_reject = require('lodash.reject');
const lo_findIndex = require('lodash.findindex');
const path = require('path');

const readdir = require('./readdir');
const util = require('./util');

const RECENT_ITEM_COUNT = 100;

const matchFunc = (filePath, stats) => {
  const ext = path.extname(filePath).toLowerCase();
  if (stats.isDirectory())
    return true;
  return (ext === '.exe' || ext === '.lnk');
};

function injectEnvVariable(dirPath) {
  let _path = dirPath;
  for (const envVar in process.env) {
    const value = process.env[envVar];
    _path = _path.replace(`\${${envVar}}`, value);
  }
  return _path;
}

function injectEnvVariables(dirArr) {
  const newArr = [];
  for (let i = 0; i < dirArr.length; ++i) {
    const dirPath = dirArr[i];
    newArr.push(injectEnvVariable(dirPath));
  }
  return newArr;
}

module.exports = (context) => {
  const logger = context.logger;
  const shell = context.shell;
  const app = context.app;
  const initialPref = context.preferences.get();
  const localStorage = context.localStorage;
  const toast = context.toast;
  let _recentUsedItems = [];

  const recursiveSearchDirs = injectEnvVariables(initialPref.recursiveFolders || []);
  const flatSearchDirs = injectEnvVariables(initialPref.flatFolders || []);

  const db = {};
  const lazyIndexingKeys = {};

  function* refreshIndex(dirs, recursive) {
    for (const dir of dirs) {
      logger.log(`refreshIndex ${dir}`);
      if (fs.existsSync(dir) === false) {
        logger.log(`can't find a dir: ${dir}`);
        continue;
      }
      const files = yield co(readdir(dir, recursive, matchFunc));
      db[dir] = files;
      logger.log(`index updated ${dir}, ${files.length} files`);
    }
  }

  function lazyRefreshIndex(dir, recursive) {
    const _lazyKey = lazyIndexingKeys[dir];
    if (_lazyKey !== undefined)
      clearTimeout(_lazyKey);

    lazyIndexingKeys[dir] = setTimeout(() => {
      co(refreshIndex([dir], recursive));
    }, 10000);
  }

  function* setupWatchers(dirs, recursive) {
    for (const dir of dirs) {
      const _dir = dir;
      fs.watch(_dir, {
        persistent: true,
        recursive: recursive
      }, (evt, filename) => {
        lazyRefreshIndex(_dir, recursive);
      });
    }
  }

  function addRecentItem(item) {
    const idx = _recentUsedItems.indexOf(item);
    if (idx >= 0)
      _recentUsedItems.splice(idx, 1);

    if (fs.existsSync(item))
      _recentUsedItems.unshift(item);

    _recentUsedItems = _recentUsedItems.slice(0, RECENT_ITEM_COUNT);
    localStorage.setItem('recentUsedItems', _recentUsedItems);
  }

  function updateRecentItems() {
    const aliveItems = [];
    for (const item of _recentUsedItems) {
      if (fs.existsSync(item))
        aliveItems.push(item);
    }
    _recentUsedItems = aliveItems;
  }

  function startup() {
    _recentUsedItems = localStorage.getItemSync('recentUsedItems') || [];
    updateRecentItems();

    co(function* () {
      yield* refreshIndex(recursiveSearchDirs, true);
      yield* refreshIndex(flatSearchDirs, false);
      yield* setupWatchers(recursiveSearchDirs, true);
      yield* setupWatchers(flatSearchDirs, false);
    }).catch((err) => {
      logger.log(err);
      logger.log(err.stack);
    });
  }

  function computeRatio(filePath) {
    let ratio = 1;
    const ext = path.extname(filePath).toLowerCase();
    const basename = path.basename(filePath).toLowerCase();
    if (ext !== '.lnk' && ext !== '.exe')
      ratio *= 0.5;
    if (ext === '.lnk')
      ratio *= 1.5;
    if (basename.indexOf('uninstall') >= 0 || basename.indexOf('remove') >= 0)
      ratio *= 0.9;
    return ratio;
  }

  function _fuzzyResultToSearchResult(results, group, fixedScore) {
    const _group = group || 'Files & Folders';
    return results.map(x => {
      const path_base64 = new Buffer(x.path).toString('base64');
      const score = (fixedScore !== undefined) ? fixedScore : x.score * computeRatio(x.path);
      return {
        id: x.path,
        title: path.basename(x.path, path.extname(x.path)),
        desc: x.html,
        icon: `icon://${path_base64}`,
        group: _group,
        score
      };
    });
  }

  function search(query, res) {
    const query_trim = query.replace(' ', '');
    const recentFuzzyResults = util.fuzzy(_recentUsedItems, query_trim).slice(0, 2);
    let recentSearchResults = [];

    if (recentFuzzyResults.length > 0)
      recentSearchResults = _fuzzyResultToSearchResult(recentFuzzyResults, 'Recent Items', 0.15);

    let fileFuzzyResults = util.fuzzy(db, query_trim).slice(0, 10);
    // Reject if it is duplicated with recent items
    fileFuzzyResults = lo_reject(fileFuzzyResults, x => lo_findIndex(recentFuzzyResults, { path: x.path }) >= 0);

    const fileSearchResults = _fuzzyResultToSearchResult(fileFuzzyResults);
    const searchResults = recentSearchResults.concat(fileSearchResults);
    res.add(searchResults);
  }

  function execute(id, payload) {
    // Update recent item, and it will be deleted if file don't exists
    addRecentItem(id);

    if (fs.existsSync(id) === false) {
      toast.enqueue('Sorry, Could\'nt Find a File');
      return;
    }

    shell.openItem(id);
    app.close();
  }

  return { startup, search, execute };
};
<commit_msg>Cleanup code<commit_after>'use strict';

const fs = require('original-fs');
const co = require('co');
const lo_reject = require('lodash.reject');
const lo_findIndex = require('lodash.findindex');
const path = require('path');

const readdir = require('./readdir');
const util = require('./util');

const RECENT_ITEM_COUNT = 100;

const matchFunc = (filePath, stats) => {
  const ext = path.extname(filePath).toLowerCase();
  if (stats.isDirectory())
    return true;
  return (ext === '.exe' || ext === '.lnk');
};

function injectEnvVariable(dirPath) {
  let _path = dirPath;
  for (const envVar in process.env) {
    const value = process.env[envVar];
    _path = _path.replace(`\${${envVar}}`, value);
  }
  return _path;
}

function injectEnvVariables(dirArr) {
  const newArr = [];
  for (let i = 0; i < dirArr.length; ++i) {
    const dirPath = dirArr[i];
    newArr.push(injectEnvVariable(dirPath));
  }
  return newArr;
}

module.exports = (context) => {
  const logger = context.logger;
  const shell = context.shell;
  const app = context.app;
  const initialPref = context.preferences.get();
  const localStorage = context.localStorage;
  const toast = context.toast;
  let _recentUsedItems = [];

  const recursiveSearchDirs = injectEnvVariables(initialPref.recursiveFolders || []);
  const flatSearchDirs = injectEnvVariables(initialPref.flatFolders || []);

  const db = {};
  const lazyIndexingKeys = {};

  function* refreshIndex(dirs, recursive) {
    for (const dir of dirs) {
      logger.log(`refreshIndex ${dir}`);
      if (fs.existsSync(dir) === false) {
        logger.log(`can't find a dir: ${dir}`);
        continue;
      }
      const files = yield co(readdir(dir, recursive, matchFunc));
      db[dir] = files;
      logger.log(`index updated ${dir}, ${files.length} files`);
    }
  }

  function lazyRefreshIndex(dir, recursive) {
    const _lazyKey = lazyIndexingKeys[dir];
    if (_lazyKey !== undefined)
      clearTimeout(_lazyKey);

    lazyIndexingKeys[dir] = setTimeout(() => {
      co(refreshIndex([dir], recursive));
    }, 10000);
  }

  function* setupWatchers(dirs, recursive) {
    for (const dir of dirs) {
      const _dir = dir;
      fs.watch(_dir, {
        persistent: true,
        recursive: recursive
      }, (evt, filename) => {
        lazyRefreshIndex(_dir, recursive);
      });
    }
  }

  function addRecentItem(item) {
    const idx = _recentUsedItems.indexOf(item);
    if (idx >= 0)
      _recentUsedItems.splice(idx, 1);

    if (fs.existsSync(item))
      _recentUsedItems.unshift(item);

    _recentUsedItems = _recentUsedItems.slice(0, RECENT_ITEM_COUNT);
    localStorage.setItem('recentUsedItems', _recentUsedItems);
  }

  function updateRecentItems() {
    const aliveItems = [];
    for (const item of _recentUsedItems) {
      if (fs.existsSync(item))
        aliveItems.push(item);
    }
    _recentUsedItems = aliveItems;
  }

  function startup() {
    _recentUsedItems = localStorage.getItemSync('recentUsedItems') || [];
    updateRecentItems();

    co(function* () {
      yield* refreshIndex(recursiveSearchDirs, true);
      yield* refreshIndex(flatSearchDirs, false);
      yield* setupWatchers(recursiveSearchDirs, true);
      yield* setupWatchers(flatSearchDirs, false);
    }).catch((err) => {
      logger.log(err);
      logger.log(err.stack);
    });
  }

  function computeRatio(filePath) {
    let ratio = 1;
    const ext = path.extname(filePath).toLowerCase();
    const basename = path.basename(filePath).toLowerCase();
    if (ext !== '.lnk' && ext !== '.exe')
      ratio *= 0.5;
    if (ext === '.lnk')
      ratio *= 1.5;
    if (basename.indexOf('uninstall') >= 0 || basename.indexOf('remove') >= 0)
      ratio *= 0.9;
    return ratio;
  }

  function _fuzzyResultToSearchResult(results, group, fixedScore) {
    const _group = group || 'Files & Folders';
    return results.map(x => {
      const path_base64 = new Buffer(x.path).toString('base64');
      const score = (fixedScore !== undefined) ? fixedScore : x.score * computeRatio(x.path);
      return {
        id: x.path,
        title: path.basename(x.path, path.extname(x.path)),
        desc: x.html,
        icon: `icon://${path_base64}`,
        group: _group,
        score
      };
    });
  }

  function search(query, res) {
    const query_trim = query.replace(' ', '');
    const recentFuzzyResults = util.fuzzy(_recentUsedItems, query_trim).slice(0, 2);
    const defaultFuzzyResults = util.fuzzy(db, query_trim).slice(0, 10);

    let recentSearchResults = [];
    if (recentFuzzyResults.length > 0)
      recentSearchResults = _fuzzyResultToSearchResult(recentFuzzyResults, 'Recent Items', 0.15);

    // Reject if it is duplicated with recent items
    const sanitizedFuzzyResults = lo_reject(defaultFuzzyResults, x => lo_findIndex(recentFuzzyResults, { path: x.path }) >= 0);
    const fileSearchResults = _fuzzyResultToSearchResult(sanitizedFuzzyResults);
    const searchResults = recentSearchResults.concat(fileSearchResults);
    res.add(searchResults);
  }

  function execute(id, payload) {
    // Update recent item, and it will be deleted if file don't exists
    addRecentItem(id);

    if (fs.existsSync(id) === false) {
      toast.enqueue('Sorry, Could\'nt Find a File');
      return;
    }

    shell.openItem(id);
    app.close();
  }

  return { startup, search, execute };
};
<|endoftext|>


<commit_before>export { Base };

import * as ValueDesc from "../../Unpacker/ValueDesc.js";
import * as TwoTensors from "../../util/TwoTensors.js";
import * as ReturnOrClone from "../ReturnOrClone.js";
//import * as TensorPlaceholder from "../TensorPlaceholder.js";
import * as Operation from "../Operation.js";
import * as BoundsArraySet from "../BoundsArraySet.js";
import { FiltersArray_BiasesArray } from "./Depthwise_FiltersArray_BiasesArray.js";

/**
 * Handle depthwise convolution, bias and activation.
 *
 * @member {boolean} bExisted
 *   If true, this depthwise operation exists. The same as this.bDepthwise.
 *
 * @member {boolean} bDepthwise
 *   If true, this depthwise operation exists. The same as this.bExisted.
 *
 * @member {boolean} bDepthwiseAvg
 *   If true, this depthwise operation exists. And it is depthwise average pooling.
 *
 * @member {boolean} bDepthwiseMax
 *   If true, this depthwise operation exists. And it is depthwise maximum pooling.
 *
 * @member {boolean} bDepthwiseConv
 *   If true, this depthwise operation exist. And it is depthwise convolution.
 *
 * @member {boolean} bInitOk
 *   If true, the init() is successful.
 *
 * @member {function} pfnOperation
 *   This is a method. It has one parameter inputTensor and return a outputTensor. The inputTensor (tf.tensor3d) represents the image
 * ( height x width x channel ) which will be processed. The outputTensor (tf.tensor3d) represents the result.
 * All intermediate tensors will be disposed. The inputTensor may or may not be disposed. In fact, this method calls one of
 * Base.return_input_directly(), Base.keep_input_return_copy(), Avg_and_destroy(), Avg_and_keep(), Max_and_destroy(), Max_and_keep(),
 * Conv_and_destroy(), Conv_and_keep() according to the parameters.
 *
 * @member {function} apply
 *   This is a method. It has one parameter inputTensor and return a outputTensor. The inputTensor (tf.tensor3d) represents the image
 * ( height x width x channel ) which will be processed. The outputTensor (tf.tensor3d) represents the result.
 * All intermediate tensors will be disposed. The inputTensors may or may not be disposed. In fact, this method calls one of
 * Base.return_input_directly(), Base.keep_input_return_copy(), Operation_and_destroy_or_keep(), OperationBias_and_destroy_or_keep(),
 * OperationActivation_and_destroy_or_keep(), OperationBiasActivation_and_destroy_or_keep() according to the parameters.
 *
 * @see FiltersArray_BiasesArray
 */
class Base extends FiltersArray_BiasesArray( TwoTensors.filtersTensor4d_biasesTensor3d( Operation.Base( ReturnOrClone.Base ) ) ) {

  /**
   */
  constructor(
    inputTensorPlaceholder0,
    inputHeight, inputWidth, inputChannelCount, AvgMax_Or_ChannelMultiplier, filterHeight, filterWidth, stridesPad,
    bBias, nActivationId, nPassThroughStyleId,
    nHigherHalfDifferent, inputChannelCount_lowerHalf ) {

    super(
      inputTensorPlaceholder0, null, 1,
      inputHeight, inputWidth, inputChannelCount, AvgMax_Or_ChannelMultiplier, filterHeight, filterWidth, stridesPad,
      bBias, nActivationId, nPassThroughStyleId,
      nHigherHalfDifferent, inputChannelCount_lowerHalf );

  }

  /**
   * @param {Float32Array} inputFloat32Array
   *   A Float32Array whose values will be interpreted as weights.
   *
   * @param {ActivationEscaping.ScaleBoundsArray} inputScaleBoundsArray
   *   The element value bounds (per channel) of input. Usually, it is The .output of the previous convolution-bias-activation value bounds
   * set of this depthwise convolution. It will be kept (not cloned) directly. So caller should not modify them.
   *
   * @return {boolean} Return true, if succeeded.
   */
  init( inputFloat32Array, byteOffsetBegin, inputScaleBoundsArray ) {

    // Q1: Why is the inputFloat32Array not a parameter of constructor?
    // A1: The reason is to avoid keeping it as this.inputFloat32Array so that it could be released by memory garbage collector.
    //
    // Q2: Why are not filtersWeights and biasesWeights kept in this?
    // A2: So that inputFloat32Array could be released.

    this.disposeTensors();

    // 1. Determine operation functions.
    Base.setup_bDepthwise_pfn.call( this );

    let bExtractOk;
    if ( !this.bDepthwise ) {
      bExtractOk = true; // 2. no operation at all. No depthwise (e.g. zero or negative number) (so no channel multiplier, too).

      this.byteOffsetBegin = this.byteOffsetEnd = byteOffsetBegin;
      this.tensorWeightCountExtracted = this.tensorWeightCountTotal = 0;

      this.boundsArraySet = new BoundsArraySet.Depthwise( inputScaleBoundsArray, inputScaleBoundsArray.channelCount );
      this.boundsArraySet.output0.set_all_byScaleBoundsArray( inputScaleBoundsArray ); // Bypass previous to next.

    } else { // 3.

      bExtractOk = super.init( inputFloat32Array, byteOffsetBegin, inputScaleBoundsArray );
      if ( bExtractOk ) {
        try {
          if ( this.filtersShape && this.filtersArray ) {
            this.filtersTensor4d = tf.tensor( this.filtersArray, this.filtersShape );
            this.filtersShape = this.filtersArray = null; // Release for reducing memory usage.
          }

          if ( this.biasesShape && this.biasesArray ) {
            this.biasesTensor3d = tf.tensor( this.biasesArray, this.biasesShape );
            this.biasesShape = this.biasesArray = null; // Release for reducing memory usage.
          }

        } catch ( e ) {  // If failed (e.g. memory not enough), return false.
          bExtractOk = false;
        }
      }

    }

    this.bInitOk = bExtractOk;
    return this.bInitOk;
  }

  /** Release tensors.
   */
  disposeTensors() {
    super.disposeTensors(); // Release filtersTensor4d and biasesTensor3d.

    this.apply = this.pfnOperation = this.pfnActivation = null;

    // If these properties does not exist, assigning value (even undefined) to them will create them. Avoid it.
    {
      if ( this.bDepthwiseAvg )
        this.bDepthwiseAvg = undefined;

      if ( this.bDepthwiseMax )
        this.bDepthwiseMax = undefined;

      if ( this.bDepthwiseConv )
        this.bDepthwiseConv = undefined;
    }

    this.bDepthwise = false;
    this.byteOffsetBegin = this.byteOffsetEnd = -1;
    this.bKeepInputTensor = false;  // Default will dispose input tensor.
    this.bInitOk = false;
  }

  /**
   * Adjust this.pfnOperation (and this.apply if need) so that this.pfnOperation() and this.apply()
   * will or will not dispose its inputTensor.
   */
  setKeepInputTensor( bKeepInputTensor ) {
    if ( bKeepInputTensor == this.bKeepInputTensor )
      return;

    this.bKeepInputTensor = bKeepInputTensor;
    if ( bKeepInputTensor ) {

      switch ( this.pfnOperation ) {

        // Just clone input if 1x1 AVG/MAX pooling or illegal pooling type (i.e. not AVG, not MAX).
        // Note: apply should not be changed here because there might be bias and activation.
        case Base.return_input_directly: this.pfnOperation = Base.keep_input_return_copy; break;

        case Base.Avg_and_destroy:       this.pfnOperation = Base.Avg_and_keep;  break;
        case Base.Max_and_destroy:       this.pfnOperation = Base.Max_and_keep;  break;
        case Base.Conv_and_destroy:      this.pfnOperation = Base.Conv_and_keep; break;

        // Just clone input if unknown depthwise operation.
        // Since there is no operation at all, let apply ignore pfnOperation completely.
        default:                         this.pfnOperation = this.apply = Base.keep_input_return_copy;
          tf.util.assert( false, `Unknown depthwise operation. (${this.pfnOperation}) when setKeepInputTensor( ${bKeepInputTensor} )` );
          break;
      }

    } else {

      switch ( this.pfnOperation ) {

        // Just return input if 1x1 AVG/MAX pooling or illegal pooling type (i.e. not AVG, not MAX).
        // Note: apply should not be changed here because there might be bias and activation.
        case Base.keep_input_return_copy: this.pfnOperation = Base.return_input_directly; break;

        case Base.Avg_and_keep:           this.pfnOperation = Base.Avg_and_destroy;  break;
        case Base.Max_and_keep:           this.pfnOperation = Base.Max_and_destroy;  break;
        case Base.Conv_and_keep:          this.pfnOperation = Base.Conv_and_destroy; break;

        // Just return input if unknown depthwise operation.
        // Since there is no operation at all, let apply ignore pfnOperation completely.
        default:                          this.pfnOperation = this.apply = Base.return_input_directly;
          tf.util.assert( false, `Unknown depthwise operation. (${this.pfnOperation}) when setKeepInputTensor( ${bKeepInputTensor} )` );
          break;
      }

    }
  }

  get bExisted() {
    return this.bDepthwise;
  }

  /** Determine this.bDepthwiseXxx and this.pfnXxx data members.
   *
   * @param {Base} this
   *   The Base object to be determined and modified.
   */
  static setup_bDepthwise_pfn() {

    // 1.
    if ( this.AvgMax_Or_ChannelMultiplier < 0 ) { // Depthwise by AVG or MAX pooling (so no channel multiplier).

      // if 1x1 AVG pooling ( and strides is 1 ), or 1x1 MAX pooling ( and strides is 1 ), or illegal pooling type (i.e. not AVG, not MAX):
      //   - As no depthwise operation (i.e. ( this.bDepthwise == false ) )
      //   - Just return input (i.e. ( this.pfnOperation == Base.return_input_directly ) )

      // When 1x1 AVG or MAX pooling (and strides is 1), the result of depthwise operation (not include bias and activation) is the same as input.
      let bOperationResultSameAsInput = ( ( 1 == this.filterHeight ) && ( 1 == this.filterWidth ) && ( 1 == this.strides ) );

      switch ( this.AvgMax_Or_ChannelMultiplier ) {
        case ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton.Ids.AVG:
          this.bDepthwise = this.bDepthwiseAvg = true;
          if ( bOperationResultSameAsInput )
            this.pfnOperation = Base.return_input_directly; // For speeding up performance. (Note: It might still has bias and/or activation.)
          else
            this.pfnOperation = Base.Avg_and_destroy;
          break;

        case ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton.Ids.MAX:
          this.bDepthwise = this.bDepthwiseMax = true;
          if ( bOperationResultSameAsInput )
            this.pfnOperation = Base.return_input_directly; // For speeding up performance. (Note: It might still has bias and/or activation.)
          else
            this.pfnOperation = Base.Max_and_destroy;
          break;
      }

    } else if ( this.AvgMax_Or_ChannelMultiplier >= 1 ) { // Depthwise by convolution (with channel multiplier).
      this.bDepthwise = this.bDepthwiseConv = true;

      this.pfnOperation = Base.Conv_and_destroy; // will dispose inputTensor.

    } else { // No depthwise (e.g. zero or negative number) (so no channel multiplier).
      this.bDepthwise = false;
    }

    // 2.
    this.pfnActivation = ValueDesc.ActivationFunction.Singleton.getInfoById( this.nActivationId );

    // 3.
    if ( this.bDepthwise ) {
      if ( this.bBias ) {
        if ( this.pfnActivation )
          this.apply = Base.OperationBiasActivation_and_destroy_or_keep;
        else
          this.apply = Base.OperationBias_and_destroy_or_keep;
      } else {
        if ( this.pfnActivation )
          this.apply = Base.OperationActivation_and_destroy_or_keep;
         else
          this.apply = Base.Operation_and_destroy_or_keep;
      }
    } else {
      // Since there is no operation at all, let apply ignore pfnOperation completely.
      this.apply = this.pfnOperation = Base.return_input_directly;
    }
  }

  /** Depthwise Average Pooling. */
  static Avg_and_keep( inputTensor ) {
    return tf.pool( inputTensor, this.poolWindowShape, "avg", this.pad, 1, this.strides ); // dilations = 1
  }

  static Avg_and_destroy( inputTensor ) {
    let t = tf.pool( inputTensor, this.poolWindowShape, "avg", this.pad, 1, this.strides ); // dilations = 1
    inputTensor.dispose();
    return t;
  }

  /** Depthwise Max Pooling. */
  static Max_and_keep( inputTensor ) {
    return tf.pool( inputTensor, this.poolWindowShape, "max", this.pad, 1, this.strides ); // dilations = 1
  }

  static Max_and_destroy( inputTensor ) {
    let t = tf.pool( inputTensor, this.poolWindowShape, "max", this.pad, 1, this.strides ); // dilations = 1
    inputTensor.dispose();
    return t;
  }

  /** Depthwise Convolution. */
  static Conv_and_keep( inputTensor ) {
    return tf.depthwiseConv2d( inputTensor, this.filtersTensor4d, this.strides, this.pad );
  }

  static Conv_and_destroy( inputTensor ) {
    let t = tf.depthwiseConv2d( inputTensor, this.filtersTensor4d, this.strides, this.pad );
    inputTensor.dispose();
    return t;
  }

//!!! ...unfinished... (2022/05/31) use this.input0 and put to this.output0

  /** Depthwise Operation, Bias and Activation. */
  static Operation_and_destroy_or_keep( inputTensor ) {
    return this.pfnOperation( inputTensor ); // may destroy or keep.
  }

  static OperationBias_and_destroy_or_keep( inputTensor ) {
    let t0 = this.pfnOperation( inputTensor ); // may destroy or keep.

    let t1 = tf.add( t0, this.biasesTensor3d );
    t0.dispose();

    return t1;
  }

  static OperationActivation_and_destroy_or_keep( inputTensor ) {
    let t0 = this.pfnOperation( inputTensor ); // may destroy or keep.

    let t1 = this.pfnActivation( t0 );
    t0.dispose();

    return t1;
  }

  static OperationBiasActivation_and_destroy_or_keep( inputTensor ) {
    let t0 = this.pfnOperation( inputTensor ); // may destroy or keep.

    let t1 = tf.add( t0, this.biasesTensor3d );
    t0.dispose();

    t0 = this.pfnActivation( t1 );
    t1.dispose();

    return t0;
  }

}
<commit_msg>Update Depthwise_Base.js<commit_after>export { Base };

import * as ValueDesc from "../../Unpacker/ValueDesc.js";
import * as TwoTensors from "../../util/TwoTensors.js";
import * as ReturnOrClone from "../ReturnOrClone.js";
//import * as TensorPlaceholder from "../TensorPlaceholder.js";
import * as Operation from "../Operation.js";
import * as BoundsArraySet from "../BoundsArraySet.js";
import { FiltersArray_BiasesArray } from "./Depthwise_FiltersArray_BiasesArray.js";

/**
 * Handle depthwise convolution, bias and activation.
 *
 * @member {boolean} bExisted
 *   If true, this depthwise operation exists. The same as this.bDepthwise.
 *
 * @member {boolean} bDepthwise
 *   If true, this depthwise operation exists. The same as this.bExisted.
 *
 * @member {boolean} bDepthwiseAvg
 *   If true, this depthwise operation exists. And it is depthwise average pooling.
 *
 * @member {boolean} bDepthwiseMax
 *   If true, this depthwise operation exists. And it is depthwise maximum pooling.
 *
 * @member {boolean} bDepthwiseConv
 *   If true, this depthwise operation exist. And it is depthwise convolution.
 *
 * @member {boolean} bInitOk
 *   If true, the init() is successful.
 *
 * @member {function} pfnOperation
 *   This is a method. It has one parameter inputTensor and return a outputTensor. The inputTensor (tf.tensor3d) represents the image
 * ( height x width x channel ) which will be processed. The outputTensor (tf.tensor3d) represents the result.
 * All intermediate tensors will be disposed. The inputTensor may or may not be disposed. In fact, this method calls one of
 * Base.return_input_directly(), Base.keep_input_return_copy(), Avg_and_destroy(), Avg_and_keep(), Max_and_destroy(), Max_and_keep(),
 * Conv_and_destroy(), Conv_and_keep() according to the parameters.
 *
 * @member {function} apply
 *   This is a method. It has one parameter inputTensor and return a outputTensor. The inputTensor (tf.tensor3d) represents the image
 * ( height x width x channel ) which will be processed. The outputTensor (tf.tensor3d) represents the result.
 * All intermediate tensors will be disposed. The inputTensors may or may not be disposed. In fact, this method calls one of
 * Base.return_input_directly(), Base.keep_input_return_copy(), Operation_and_destroy_or_keep(), OperationBias_and_destroy_or_keep(),
 * OperationActivation_and_destroy_or_keep(), OperationBiasActivation_and_destroy_or_keep() according to the parameters.
 *
 * @see FiltersArray_BiasesArray
 */
class Base extends FiltersArray_BiasesArray( TwoTensors.filtersTensor4d_biasesTensor3d( Operation.Base( ReturnOrClone.Base ) ) ) {

  /**
   */
  constructor(
    inputTensorPlaceholder0,
    inputHeight, inputWidth, inputChannelCount, AvgMax_Or_ChannelMultiplier, filterHeight, filterWidth, stridesPad,
    bBias, nActivationId, nPassThroughStyleId,
    nHigherHalfDifferent, inputChannelCount_lowerHalf ) {

    super(
      inputTensorPlaceholder0, null, 1,
      inputHeight, inputWidth, inputChannelCount, AvgMax_Or_ChannelMultiplier, filterHeight, filterWidth, stridesPad,
      bBias, nActivationId, nPassThroughStyleId,
      nHigherHalfDifferent, inputChannelCount_lowerHalf );

  }

  /**
   * @param {Float32Array} inputFloat32Array
   *   A Float32Array whose values will be interpreted as weights.
   *
   * @param {ActivationEscaping.ScaleBoundsArray} inputScaleBoundsArray
   *   The element value bounds (per channel) of input. Usually, it is The .output of the previous convolution-bias-activation value bounds
   * set of this depthwise convolution. It will be kept (not cloned) directly. So caller should not modify them.
   *
   * @return {boolean} Return true, if succeeded.
   */
  init( inputFloat32Array, byteOffsetBegin, inputScaleBoundsArray ) {

    // Q1: Why is the inputFloat32Array not a parameter of constructor?
    // A1: The reason is to avoid keeping it as this.inputFloat32Array so that it could be released by memory garbage collector.
    //
    // Q2: Why are not filtersWeights and biasesWeights kept in this?
    // A2: So that inputFloat32Array could be released.

    this.disposeTensors();

    // 1. Determine operation functions.
    Base.setup_bDepthwise_pfn.call( this );

    let bExtractOk;
    if ( !this.bDepthwise ) {
      bExtractOk = true; // 2. no operation at all. No depthwise (e.g. zero or negative number) (so no channel multiplier, too).

      this.byteOffsetBegin = this.byteOffsetEnd = byteOffsetBegin;
      this.tensorWeightCountExtracted = this.tensorWeightCountTotal = 0;

      this.boundsArraySet = new BoundsArraySet.Depthwise( inputScaleBoundsArray, inputScaleBoundsArray.channelCount );
      this.boundsArraySet.output0.set_all_byScaleBoundsArray( inputScaleBoundsArray ); // Bypass previous to next.

    } else { // 3.

      bExtractOk = super.init( inputFloat32Array, byteOffsetBegin, inputScaleBoundsArray );
      if ( bExtractOk ) {
        try {
          if ( this.filtersShape && this.filtersArray ) {
            this.filtersTensor4d = tf.tensor( this.filtersArray, this.filtersShape );
            this.filtersShape = this.filtersArray = null; // Release for reducing memory usage.
          }

          if ( this.biasesShape && this.biasesArray ) {
            this.biasesTensor3d = tf.tensor( this.biasesArray, this.biasesShape );
            this.biasesShape = this.biasesArray = null; // Release for reducing memory usage.
          }

        } catch ( e ) {  // If failed (e.g. memory not enough), return false.
          bExtractOk = false;
        }
      }

    }

    this.bInitOk = bExtractOk;
    return this.bInitOk;
  }

  /** Release tensors.
   */
  disposeTensors() {
    super.disposeTensors(); // Release filtersTensor4d and biasesTensor3d.

    this.apply = this.pfnOperation = this.pfnActivation = null;

    // If these properties does not exist, assigning value (even undefined) to them will create them. Avoid it.
    {
      if ( this.bDepthwiseAvg )
        this.bDepthwiseAvg = undefined;

      if ( this.bDepthwiseMax )
        this.bDepthwiseMax = undefined;

      if ( this.bDepthwiseConv )
        this.bDepthwiseConv = undefined;
    }

    this.bDepthwise = false;
    this.byteOffsetBegin = this.byteOffsetEnd = -1;
    this.bKeepInputTensor = false;  // Default will dispose input tensor.
    this.bInitOk = false;
  }

  /**
   * Adjust this.pfnOperation (and this.apply if need) so that this.pfnOperation() and this.apply()
   * will or will not dispose its inputTensor.
   */
  setKeepInputTensor( bKeepInputTensor ) {
    if ( bKeepInputTensor == this.bKeepInputTensor )
      return;

    this.bKeepInputTensor = bKeepInputTensor;
    if ( bKeepInputTensor ) {

      switch ( this.pfnOperation ) {

        // Just clone input if 1x1 AVG/MAX pooling or illegal pooling type (i.e. not AVG, not MAX).
        // Note: apply should not be changed here because there might be bias and activation.
        case Base.return_input_directly: this.pfnOperation = Base.keep_input_return_copy; break;

        case Base.Avg_and_destroy:       this.pfnOperation = Base.Avg_and_keep;  break;
        case Base.Max_and_destroy:       this.pfnOperation = Base.Max_and_keep;  break;
        case Base.Conv_and_destroy:      this.pfnOperation = Base.Conv_and_keep; break;

        // Just clone input if unknown depthwise operation.
        // Since there is no operation at all, let apply ignore pfnOperation completely.
        default:                         this.pfnOperation = this.apply = Base.keep_input_return_copy;
          tf.util.assert( false, `Unknown depthwise operation. (${this.pfnOperation}) when setKeepInputTensor( ${bKeepInputTensor} )` );
          break;
      }

    } else {

      switch ( this.pfnOperation ) {

        // Just return input if 1x1 AVG/MAX pooling or illegal pooling type (i.e. not AVG, not MAX).
        // Note: apply should not be changed here because there might be bias and activation.
        case Base.keep_input_return_copy: this.pfnOperation = Base.return_input_directly; break;

        case Base.Avg_and_keep:           this.pfnOperation = Base.Avg_and_destroy;  break;
        case Base.Max_and_keep:           this.pfnOperation = Base.Max_and_destroy;  break;
        case Base.Conv_and_keep:          this.pfnOperation = Base.Conv_and_destroy; break;

        // Just return input if unknown depthwise operation.
        // Since there is no operation at all, let apply ignore pfnOperation completely.
        default:                          this.pfnOperation = this.apply = Base.return_input_directly;
          tf.util.assert( false, `Unknown depthwise operation. (${this.pfnOperation}) when setKeepInputTensor( ${bKeepInputTensor} )` );
          break;
      }

    }
  }

  get bExisted() {
    return this.bDepthwise;
  }

  /** Determine this.bDepthwiseXxx and this.pfnXxx data members.
   *
   * @param {Base} this
   *   The Base object to be determined and modified.
   */
  static setup_bDepthwise_pfn() {

    // 1.
    if ( this.AvgMax_Or_ChannelMultiplier < 0 ) { // Depthwise by AVG or MAX pooling (so no channel multiplier).

      // if 1x1 AVG pooling ( and strides is 1 ), or 1x1 MAX pooling ( and strides is 1 ), or illegal pooling type (i.e. not AVG, not MAX):
      //   - As no depthwise operation (i.e. ( this.bDepthwise == false ) )
      //   - Just return input (i.e. ( this.pfnOperation == Base.return_input_directly ) )

      // When 1x1 AVG or MAX pooling (and strides is 1), the result of depthwise operation (not include bias and activation) is the same as input.
      let bOperationResultSameAsInput = ( ( 1 == this.filterHeight ) && ( 1 == this.filterWidth ) && ( 1 == this.strides ) );

      switch ( this.AvgMax_Or_ChannelMultiplier ) {
        case ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton.Ids.AVG:
          this.bDepthwise = this.bDepthwiseAvg = true;
          if ( bOperationResultSameAsInput )
            this.pfnOperation = Base.return_input_directly; // For speeding up performance. (Note: It might still has bias and/or activation.)
          else
            this.pfnOperation = Base.Avg_and_destroy;
          break;

        case ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton.Ids.MAX:
          this.bDepthwise = this.bDepthwiseMax = true;
          if ( bOperationResultSameAsInput )
            this.pfnOperation = Base.return_input_directly; // For speeding up performance. (Note: It might still has bias and/or activation.)
          else
            this.pfnOperation = Base.Max_and_destroy;
          break;
      }

    } else if ( this.AvgMax_Or_ChannelMultiplier >= 1 ) { // Depthwise by convolution (with channel multiplier).
      this.bDepthwise = this.bDepthwiseConv = true;

      this.pfnOperation = Base.Conv_and_destroy; // will dispose inputTensor.

    } else { // No depthwise (e.g. zero or negative number) (so no channel multiplier).
      this.bDepthwise = false;
    }

    // 2.
    this.pfnActivation = ValueDesc.ActivationFunction.Singleton.getInfoById( this.nActivationId )?.pfn;

    // 3.
    if ( this.bDepthwise ) {
      if ( this.bBias ) {
        if ( this.pfnActivation )
          this.apply = Base.OperationBiasActivation_and_destroy_or_keep;
        else
          this.apply = Base.OperationBias_and_destroy_or_keep;
      } else {
        if ( this.pfnActivation )
          this.apply = Base.OperationActivation_and_destroy_or_keep;
         else
          this.apply = Base.Operation_and_destroy_or_keep;
      }
    } else {
      // Since there is no operation at all, let apply ignore pfnOperation completely.
      this.apply = this.pfnOperation = Base.return_input_directly;
    }
  }

  /** Depthwise Average Pooling. */
  static Avg_and_keep( inputTensor ) {
    return tf.pool( inputTensor, this.poolWindowShape, "avg", this.pad, 1, this.strides ); // dilations = 1
  }

  static Avg_and_destroy( inputTensor ) {
    let t = tf.pool( inputTensor, this.poolWindowShape, "avg", this.pad, 1, this.strides ); // dilations = 1
    inputTensor.dispose();
    return t;
  }

  /** Depthwise Max Pooling. */
  static Max_and_keep( inputTensor ) {
    return tf.pool( inputTensor, this.poolWindowShape, "max", this.pad, 1, this.strides ); // dilations = 1
  }

  static Max_and_destroy( inputTensor ) {
    let t = tf.pool( inputTensor, this.poolWindowShape, "max", this.pad, 1, this.strides ); // dilations = 1
    inputTensor.dispose();
    return t;
  }

  /** Depthwise Convolution. */
  static Conv_and_keep( inputTensor ) {
    return tf.depthwiseConv2d( inputTensor, this.filtersTensor4d, this.strides, this.pad );
  }

  static Conv_and_destroy( inputTensor ) {
    let t = tf.depthwiseConv2d( inputTensor, this.filtersTensor4d, this.strides, this.pad );
    inputTensor.dispose();
    return t;
  }

//!!! ...unfinished... (2022/05/31) use this.input0 and put to this.output0

  /** Depthwise Operation, Bias and Activation. */
  static Operation_and_destroy_or_keep( inputTensor ) {
    return this.pfnOperation( inputTensor ); // may destroy or keep.
  }

  static OperationBias_and_destroy_or_keep( inputTensor ) {
    let t0 = this.pfnOperation( inputTensor ); // may destroy or keep.

    let t1 = tf.add( t0, this.biasesTensor3d );
    t0.dispose();

    return t1;
  }

  static OperationActivation_and_destroy_or_keep( inputTensor ) {
    let t0 = this.pfnOperation( inputTensor ); // may destroy or keep.

    let t1 = this.pfnActivation( t0 );
    t0.dispose();

    return t1;
  }

  static OperationBiasActivation_and_destroy_or_keep( inputTensor ) {
    let t0 = this.pfnOperation( inputTensor ); // may destroy or keep.

    let t1 = tf.add( t0, this.biasesTensor3d );
    t0.dispose();

    t0 = this.pfnActivation( t1 );
    t1.dispose();

    return t0;
  }

}
<|endoftext|>