<commit_before>
/*
================

================
*/
var Result = function(status,output,settings,exceptions){
  var _status=status;
  var _output=output;
  var _settings=settings;
  var _exceptions=exceptions;
  /*-- this.status --*/
  this.status = function(){
    return _status;
  };
  /*-- this.output --*/
  this.output = function(){
    return _output;
  };
  /*-- this.settings --*/
  this.settings = function(){
    return _settings;
  };
  /*-- this.exceptions --*/
  this.exceptions = function(){
    return _exceptions;
  };
};
/*
================

================
*/
var Chain = function(){
  var _defaults = {};
  var _options = {};
  var _defaultHandlers = {};
  var _customHandlers = {};
  var _defaultWorkers = {};
  var _customWorkers = {};
  var _handlerFirst = true;
  var _customHandlerFirst = true;
  var _customWorkerFirst = true;
  var _optionFirst = true;
  /*
  ====================================
  -- access utility method --
  ====================================
  */
  var _access = function(target,callerArguments){
    var args = callerArguments ||[];
    //alert('cLength : ' + args.length);
    if(args.length === 1){
      return typeof target === 'undefined' || target === null ?
          undefined : [args[0],target[args[0]]] ;
    } else if (args.length >= 2){
      // more than one
      if(typeof target === 'undefined' || target === null)
        target = {};
      target[args[0]] = args[1];
    }
    //no argument
    return target;
  };
  /*
  ====================================
  -- compound access utility method --
  ====================================
  */
  var _compoundAccess = function(couple,defaultTarget, customTarget,callerArguments){
    if(!arguments || arguments.length < 3)
      return undefined;
    var args = callerArguments||[];

    if(!args.length){
      return {defaults : _access(defaultTarget,args),
            options : _access(customTarget,args)};
    }

    var defaultResult = _access(defaultTarget, args)[1];
    var customResult = _access(customTarget, args)[1];
    // when one is null and other undefined vice versa
    // return null.
    if(defaultResult === null && typeof customResult === 'undefined' ||
      typeof defaultResult === 'undefined' && customResult === null)
      return null;
    return couple === 'settings' && _optionFirst ||
        couple === 'handlers' && _customHandlerFirst ||
        couple === 'workers' && _customWorkerFirst ?
        customResult ||defaultResult :
        defaultResult||customResult;
  };
  /*
  ====================================
  -- this.defaults method --
  ====================================
  */
  this.defaults = function(){
    //alert('defaults : ' + JSON.stringify(_defaults));
    var result = _access(_defaults, arguments);
    //result !== arguments[0] : when catalog === array
    if(Array.isArray(result) && result !== arguments[0])
      return result[1];
    else
      _defaults = result;
    return _defaults;
  };
  /*
  ====================================
  -- this.defaultsReset method --
  ====================================
  */
  this.defaultsReset = function(){
    if(arguments.length)
      _defaults = arguments[0];
    return _defaults;
  };
  /*
  ====================================
  -- this.options method --
  ====================================
  */
  this.options = function(){
    var result = _access(_options, arguments);
    //result !== arguments[0] : when catalog === array
    if(Array.isArray(result) && result !== arguments[0])
      return result[1];
    else
      _options = result;
    return _options;
  };
  /*
  ====================================
  -- this.optionsReset method --
  ====================================
  */
  this.optionsReset = function(){
    if(arguments.length)
      _options = arguments[0];
    return _options;
  };
  /*
  ====================================
  -- this.settings method --
  ====================================
  */
  this.settings = function(){
    //alert(JSON.stringify(arguments));
    return _compoundAccess('settings',_defaults,_options,arguments);
  };
  /*
  ====================================
  -- this.defaultWorkers method --
  ====================================
  */
  this.defaultWorkers = function(){
    var result = _access(_defaultWorkers, arguments);
    //result !== arguments[0] : when catalog === array
    if(Array.isArray(result) && result !== arguments[0])
      return result[1];
    else
      _defaultWorkers = result;
    return _defaultWorkers;
  };
  /*
  ====================================
  -- this.defaultWorkersReset method --
  ====================================
  */
  this.defaultWorkersReset = function(){
    if(arguments.length)
      _defaultWorkers = arguments[0];
    return _defaultWorkers;
  };
  /*
  ====================================
  -- this.customWorkers method --
  ====================================
  */
  this.customWorkers = function(){
    var result = _access(_customWorkers, arguments);
    //result !== arguments[0] : when catalog === array
    if(Array.isArray(result) && result !== arguments[0])
      return result[1];
    else
      _customWorkers = result;
    return _customWorkers;
  };
  /*
  ====================================
  -- this.customWorkersReset method --
  ====================================
  */
  this.customWorkersReset = function(){
    if(arguments.length)
      _customWorkers = arguments[0];
    return _customWorkers;
  };
  /*
  ====================================
  -- this.workers method --
  ====================================
  */
  /*-- this.workers --*/
  this.workers = function(){
    return _compoundAccess('workers',_defaultWorkers,_customWorkers,arguments);
  };
  /*
  ====================================
  -- this.defaultHandlers method --
  ====================================
  */
  this.defaultHandlers = function(){
    var result = _access(_defaultHandlers, arguments);
    //result !== arguments[0] : when catalog === array
    if(Array.isArray(result) && result !== arguments[0])
      return result[1];
    else
      _defaultHandlers = result;
    return _defaultHandlers;
  };
  /*
  ====================================
  -- this.defaultHandlersReset method --
  ====================================
  */
  this.defaultHandlersReset = function(){
    if(arguments.length)
      _defaultHandlers = arguments[0];
    return _defaultHandlers;
  };
  /*
  ====================================
  -- this.customHandlers method --
  ====================================
  */
  this.customHandlers = function(){
    var result = _access(_customHandlers, arguments);
    //result !== arguments[0] : when catalog === array
    if(Array.isArray(result) && result !== arguments[0])
      return result[1];
    else
      _customHandlers = result;
    return _customHandlers;
  };
  /*
  ====================================
  -- this.defaultHandlersReset method --
  ====================================
  */
  this.customHandlersReset = function(){
    if(arguments.length)
      _customHandlers = arguments[0];
    return _customHandlers;
  };
  /*
  ====================================
  -- this.handlers method --
  ====================================
  */
  this.handlers = function(){
    return _compoundAccess('handlers',_defaultHandlers,_customHandlers,arguments);
  };
  /*
  ====================================
  -- this.defaultHandlersReset method --
  ====================================
  */
  this.catalogsReset = function(){
    if(!arguments.length)
      return;
    var args = arguments;
    var catalogs =
        ['defaults','options','defaultHandlers',
         'customHandlers','defaultWorkers','customWorkers'];
    catalogs.forEach(function(current,index, array){
      if(args[0].hasOwnProperty(current))
        switch (current){
          case catalogs[0] ://alert('in catalogsReset : ' + arguments[0][current]);
            _defaults = args[0][current];
            break;
          case catalogs[1] :
            _options = args[0][current];
            break;
          case catalogs[2] :
            _defaultHandlers = args[0][current];
            break;
          case catalogs[3] :
            _customHandlers = args[0][current];
            break;
          case catalogs[4] :
            _defaultWorkers = args[0][current];
            break;
          case catalogs[5] :
            _customWorkers = args[0][current];
            break;
        }
    });
  };
  /*
  ====================================
  -- this.handlerFirst method --
  ====================================
  */
  this.handlerFirst = function(){
    if(arguments.length)
      _handlerFirst = Boolean(arguments[0]);
    return _handlerFirst;
  };
  /*
  ====================================
  -- this.customHandlerFirst --
  ====================================
  */
  this.customHandlerFirst = function(){
    if(arguments.length)
      _customHandlerFirst = Boolean(arguments[0]);
    return _customHandlerFirst;
  };
  /*
  ====================================
  -- this.customWorkerFirst --
  ====================================
  */
  this.customWorkerFirst = function(){
    if(arguments.length)
      _customWorkerFirst = Boolean(arguments[0]);
    return _customWorkerFirst;
  };
  /*
  ====================================
  -- this.optionFirst --
  ====================================
  */
  this.optionFirst = function(){
    if(arguments.length)
      _optionFirst = Boolean(arguments[0]);
    return _optionFirst;
  };
  /*
  ====================================
  -- runWorker method --
  ====================================
  */
  var runWorker = function(work,workName,settings){
    if(work && typeof work === 'function'){
      var output;
      var exceptions;
      // simple function to execute
      try {
        output = work(settings);
      } catch(error){
        error.message = '[WorkerError for workName "' +
          workName + '"] : ' + error.message;
        error.name = 'workerError';
        exceptions = error;
      }
      return new Result('ok',output,settings,exceptions);
    } else
      return undefined;
  };
  /*
  ====================================
  -- runWorkerFirst method --
  ====================================
  */
  var runHandler = function(handler,workName,settings){
    if(handler && handler.handle && typeof handler.handle === 'function'){
      var output;
      var exceptions;
      var status = 'ok';
      // handle function to execute on a object
      try{
        output = handler.handle(settings);
      } catch(error){
        error.message = '[HandlerError for workname "' + workName +
          '"] : ' + error.message;
        error.name = 'handlerError';
        exceptions = error;
      }
      // ??? what if output null/undefined ?
      return output instanceof Result ?
          output : new Result(status,output,settings,exceptions);
    } else
      return undefined;
  };
  /*
  ====================================
  -- this.run method --
  ====================================
  */
  this.run = function(workName,parameters){
    var settings = this.optionFirst() ?
        Object.assign({},this.defaults(),this.options(),parameters||{}) :
    Object.assign({},this.options(),parameters||{},this.defaults());
    var handler = this.handlers(workName||undefined);
    var work = this.workers(workName||undefined);
    var result =
        this.handlerFirst() ?
        runHandler(handler, workName,settings) ||
        runWorker(work,workName,settings)
    :  runWorker(work,workName,settings) ||
        runHandler(handler, workName,settings);
    if(result)
      return result;
    // undefined result
    var unknownWorknameError = new Error(
        '[UnknownWorknameError for workname "' +
      workName + '"] : no handler and no worker function found.');
    unknownWorknameError.name = 'unknownWorknameError';
    // return
    return new Result('ok',undefined,settings,unknownWorknameError);
  };
};
<commit_msg>+ Feature : selection of actives entries<commit_after>
/*
================

================
*/
var Result = function(status,output,settings,exceptions){
  var _status=status;
  var _output=output;
  var _settings=settings;
  var _exceptions=exceptions;
  /*-- this.status --*/
  this.status = function(){
    return _status;
  };
  /*-- this.output --*/
  this.output = function(){
    return _output;
  };
  /*-- this.settings --*/
  this.settings = function(){
    return _settings;
  };
  /*-- this.exceptions --*/
  this.exceptions = function(){
    return _exceptions;
  };
};
/*
================

================
*/
var Chain = function(){
  var _defaults = {};
  var _options = {};
  var _defaultHandlers = {};
  var _customHandlers = {};
  var _defaultWorkers = {};
  var _customWorkers = {};
  var _handlerFirst = true;
  var _customHandlerFirst = true;
  var _customWorkerFirst = true;
  var _optionFirst = true;
  /*
  ====================================
  -- access utility method --
  ====================================
  */
  var _access = function(target,callerArguments){
    var args = callerArguments ||[];
    //alert('cLength : ' + args.length);
    if(args.length === 1){
      return typeof target === 'undefined' || target === null ?
          undefined : [args[0],target[args[0]]] ;
    } else if (args.length >= 2){
      // more than one
      if(typeof target === 'undefined' || target === null)
        target = {};
      target[args[0]] = args[1];
    }
    //no argument
    return target;
  };
  /*
  ====================================
  -- compound access utility method --
  ====================================
  */
  var _compoundAccess = function(couple,defaultTarget, customTarget,callerArguments){
    if(!arguments || arguments.length < 3)
      return undefined;
    var args = callerArguments||[];

    if(!args.length){
      return {defaults : _access(defaultTarget,args),
            options : _access(customTarget,args)};
    }

    var defaultResult = _access(defaultTarget, args)[1];
    var customResult = _access(customTarget, args)[1];
    // when one is null and other undefined vice versa
    // return null.
    if(defaultResult === null && typeof customResult === 'undefined' ||
      typeof defaultResult === 'undefined' && customResult === null)
      return null;
    return couple === 'settings' && _optionFirst ||
        couple === 'handlers' && _customHandlerFirst ||
        couple === 'workers' && _customWorkerFirst ?
        customResult ||defaultResult :
        defaultResult||customResult;
  };
  /*
  ====================================
  -- this.settings method --
  ====================================
  */
  _selectActive = function(booleanSwitch,defaults,options,parameters){
    //alert(JSON.stringify(arguments));
    return booleanSwitch ?
      Object.assign({},defaults||{},options||{},parameters||{}) :
      Object.assign({},options||{},parameters||{},defaults||{});
  };
  /*
  ====================================
  -- this.defaults method --
  ====================================
  */
  this.defaults = function(){
    //alert('defaults : ' + JSON.stringify(_defaults));
    var result = _access(_defaults, arguments);
    //result !== arguments[0] : when catalog === array
    if(Array.isArray(result) && result !== arguments[0])
      return result[1];
    else
      _defaults = result;
    return _defaults;
  };
  /*
  ====================================
  -- this.defaultsReset method --
  ====================================
  */
  this.defaultsReset = function(){
    if(arguments.length)
      _defaults = arguments[0];
    return _defaults;
  };
  /*
  ====================================
  -- this.options method --
  ====================================
  */
  this.options = function(){
    var result = _access(_options, arguments);
    //result !== arguments[0] : when catalog === array
    if(Array.isArray(result) && result !== arguments[0])
      return result[1];
    else
      _options = result;
    return _options;
  };
  /*
  ====================================
  -- this.optionsReset method --
  ====================================
  */
  this.optionsReset = function(){
    if(arguments.length)
      _options = arguments[0];
    return _options;
  };
  /*
  ====================================
  -- this.settings method --
  ====================================
  */
  this.settings = function(){
    //alert(JSON.stringify(arguments));
    return _compoundAccess('settings',_defaults,_options,arguments);
  };
  /*
  ====================================
  -- this.settings method --
  ====================================
  */
  this.activeSettings = function(parameters){
    //alert(JSON.stringify(arguments));
    return _selectActive(_optionFirst,_options,_defaults,
                         parameters||undefined);
  };
  /*
  ====================================
  -- this.defaultWorkers method --
  ====================================
  */
  this.defaultWorkers = function(){
    var result = _access(_defaultWorkers, arguments);
    //result !== arguments[0] : when catalog === array
    if(Array.isArray(result) && result !== arguments[0])
      return result[1];
    else
      _defaultWorkers = result;
    return _defaultWorkers;
  };
  /*
  ====================================
  -- this.defaultWorkersReset method --
  ====================================
  */
  this.defaultWorkersReset = function(){
    if(arguments.length)
      _defaultWorkers = arguments[0];
    return _defaultWorkers;
  };
  /*
  ====================================
  -- this.customWorkers method --
  ====================================
  */
  this.customWorkers = function(){
    var result = _access(_customWorkers, arguments);
    //result !== arguments[0] : when catalog === array
    if(Array.isArray(result) && result !== arguments[0])
      return result[1];
    else
      _customWorkers = result;
    return _customWorkers;
  };
  /*
  ====================================
  -- this.customWorkersReset method --
  ====================================
  */
  this.customWorkersReset = function(){
    if(arguments.length)
      _customWorkers = arguments[0];
    return _customWorkers;
  };
  /*
  ====================================
  -- this.workers method --
  ====================================
  */
  /*-- this.workers --*/
  this.workers = function(){
    return _compoundAccess('workers',_defaultWorkers,_customWorkers,arguments);
  };
  /*
  ====================================
  -- this.activeWorkers method --
  ====================================
  */
  /*-- this.workers --*/
  this.activeWorkers = function(paramters){
    return _selectActive(_customWorkerFirst,_customWorkers,
                         _defaultWorkers,paramters||undefined);
  };
  /*
  ====================================
  -- this.defaultHandlers method --
  ====================================
  */
  this.defaultHandlers = function(){
    var result = _access(_defaultHandlers, arguments);
    //result !== arguments[0] : when catalog === array
    if(Array.isArray(result) && result !== arguments[0])
      return result[1];
    else
      _defaultHandlers = result;
    return _defaultHandlers;
  };
  /*
  ====================================
  -- this.defaultHandlersReset method --
  ====================================
  */
  this.defaultHandlersReset = function(){
    if(arguments.length)
      _defaultHandlers = arguments[0];
    return _defaultHandlers;
  };
  /*
  ====================================
  -- this.customHandlers method --
  ====================================
  */
  this.customHandlers = function(){
    var result = _access(_customHandlers, arguments);
    //result !== arguments[0] : when catalog === array
    if(Array.isArray(result) && result !== arguments[0])
      return result[1];
    else
      _customHandlers = result;
    return _customHandlers;
  };
  /*
  ====================================
  -- this.defaultHandlersReset method --
  ====================================
  */
  this.customHandlersReset = function(){
    if(arguments.length)
      _customHandlers = arguments[0];
    return _customHandlers;
  };
  /*
  ====================================
  -- this.handlers method --
  ====================================
  */
  this.handlers = function(){
    return _compoundAccess('handlers',_defaultHandlers,_customHandlers,arguments);
  };
  /*
  ====================================
  -- this.handlers method --
  ====================================
  */
  this.activeHandlers = function(parameters){
    return _selectActive(_customHandlerFirst,_customHandlers,
                         _defaultHandlers,parameters||undefined);
  };
  /*
  ====================================
  -- this.defaultHandlersReset method --
  ====================================
  */
  this.catalogsReset = function(){
    if(!arguments.length)
      return;
    var args = arguments;
    var catalogs =
        ['defaults','options','defaultHandlers',
         'customHandlers','defaultWorkers','customWorkers'];
    catalogs.forEach(function(current,index, array){
      if(args[0].hasOwnProperty(current))
        switch (current){
          case catalogs[0] ://alert('in catalogsReset : ' + arguments[0][current]);
            _defaults = args[0][current];
            break;
          case catalogs[1] :
            _options = args[0][current];
            break;
          case catalogs[2] :
            _defaultHandlers = args[0][current];
            break;
          case catalogs[3] :
            _customHandlers = args[0][current];
            break;
          case catalogs[4] :
            _defaultWorkers = args[0][current];
            break;
          case catalogs[5] :
            _customWorkers = args[0][current];
            break;
        }
    });
  };
  /*
  ====================================
  -- this.handlerFirst method --
  ====================================
  */
  this.handlerFirst = function(){
    if(arguments.length)
      _handlerFirst = Boolean(arguments[0]);
    return _handlerFirst;
  };
  /*
  ====================================
  -- this.customHandlerFirst --
  ====================================
  */
  this.customHandlerFirst = function(){
    if(arguments.length)
      _customHandlerFirst = Boolean(arguments[0]);
    return _customHandlerFirst;
  };
  /*
  ====================================
  -- this.customWorkerFirst --
  ====================================
  */
  this.customWorkerFirst = function(){
    if(arguments.length)
      _customWorkerFirst = Boolean(arguments[0]);
    return _customWorkerFirst;
  };
  /*
  ====================================
  -- this.optionFirst --
  ====================================
  */
  this.optionFirst = function(){
    if(arguments.length)
      _optionFirst = Boolean(arguments[0]);
    return _optionFirst;
  };
  /*
  ====================================
  -- runWorker method --
  ====================================
  */
  var runWorker = function(work,workName,settings){
    if(work && typeof work === 'function'){
      var output;
      var exceptions;
      // simple function to execute
      try {
        output = work(settings);
      } catch(error){
        error.message = '[WorkerError for workName "' +
          workName + '"] : ' + error.message;
        error.name = 'workerError';
        exceptions = error;
      }
      return new Result('ok',output,settings,exceptions);
    } else
      return undefined;
  };
  /*
  ====================================
  -- runWorkerFirst method --
  ====================================
  */
  var runHandler = function(handler,workName,settings){
    if(handler && handler.handle && typeof handler.handle === 'function'){
      var output;
      var exceptions;
      var status = 'ok';
      // handle function to execute on a object
      try{
        output = handler.handle(settings);
      } catch(error){
        error.message = '[HandlerError for workname "' + workName +
          '"] : ' + error.message;
        error.name = 'handlerError';
        exceptions = error;
      }
      // ??? what if output null/undefined ?
      return output instanceof Result ?
          output : new Result(status,output,settings,exceptions);
    } else
      return undefined;
  };
  /*
  ====================================
  -- this.run method --
  ====================================
  */
  this.run = function(workName,parameters){
    var settings = this.activeSettings(parameters||undefined);
    var handler = this.handlers(workName||undefined);
    var work = this.workers(workName||undefined);
    var result =
        this.handlerFirst() ?
        runHandler(handler, workName,settings) ||
        runWorker(work,workName,settings)
    :  runWorker(work,workName,settings) ||
        runHandler(handler, workName,settings);
    if(result)
      return result;
    // undefined result
    var unknownWorknameError = new Error(
        '[UnknownWorknameError for workname "' +
      workName + '"] : no handler and no worker function found.');
    unknownWorknameError.name = 'unknownWorknameError';
    // return
    return new Result('ok',undefined,settings,unknownWorknameError);
  };
};
<|endoftext|>


<commit_before>import { expect } from "chai";
import RxPlayer from "../../src";
import { manifestInfos } from "../contents/DASH_static_SegmentTimeline";
import textTrackInfos from "../contents/texttracks";
import imageInfos from "../contents/imagetracks";
import sleep from "../utils/sleep.js";
import waitForPlayerState, {
  waitForLoadedStateAfterLoadVideo,
} from "../utils/waitForPlayerState";

let player;

describe("Memory tests", () => {
  afterEach(() => {
    if (player != null) {
      player.dispose();
    }
  });

  it("should not have a sensible memory leak after playing a content", async function() {
    if (window.performance == null ||
        window.performance.memory == null ||
        window.gc == null)
    {
      // eslint-disable-next-line no-console
      console.warn("API not available. Skipping test.");
      return;
    }
    this.timeout(5 * 60 * 1000);
    player = new RxPlayer({ initialVideoBitrate: Infinity,
                            initialAudioBitrate: Infinity,
                            preferredTextTracks: [{ language: "fra",
                                                    closedCaption: true }] });
    window.gc();
    const initialMemory = window.performance.memory;

    player.loadVideo({ url: manifestInfos.url,
                       transport: manifestInfos.transport,
                       supplementaryTextTracks: [{ url: textTrackInfos.url,
                                                   language: "fra",
                                                   mimeType: "application/ttml+xml",
                                                   closedCaption: true }],
                       supplementaryImageTracks: [{ mimeType: "application/bif",
                                                    url: imageInfos.url }],
                       autoPlay: true });
    player.setPlaybackRate(4);
    await waitForPlayerState(player, "ENDED");

    player.stop();
    await sleep(1000);
    window.gc();
    const newMemory = window.performance.memory;
    const heapDifference = newMemory.usedJSHeapSize -
                           initialMemory.usedJSHeapSize;

    // eslint-disable-next-line no-console
    console.log(`
      ===========================================================
      | Current heap usage (B) | ${newMemory.usedJSHeapSize}
      | Initial heap usage (B) | ${initialMemory.usedJSHeapSize}
      | Difference (B)         | ${heapDifference}
    `);
    expect(heapDifference).to.be.below(1e7);
  });

  it("should not have a sensible memory leak after 1000 LOADED states and adaptive streaming", async function() {
    if (window.performance == null ||
        window.performance.memory == null ||
        window.gc == null)
    {
      // eslint-disable-next-line no-console
      console.warn("API not available. Skipping test.");
      return;
    }
    this.timeout(5 * 60 * 1000);
    player = new RxPlayer({ initialVideoBitrate: Infinity,
                            initialaudiobitrate: Infinity,
                            preferredtexttracks: [{ language: "fra",
                                                    closedcaption: true }] });
    window.gc();
    const initialMemory = window.performance.memory;

    for (let i = 0; i < 1000; i++) {
      player.loadVideo({ url: manifestInfos.url,
                         transport: manifestInfos.transport,
                         supplementaryTextTracks: [{ url: textTrackInfos.url,
                                                     language: "fra",
                                                     mimeType: "application/ttml+xml",
                                                     closedCaption: true }],
                         supplementaryImageTracks: [{ mimeType: "application/bif",
                                                      url: imageInfos.url }],
                         autoPlay: true });
      await waitForLoadedStateAfterLoadVideo(player);
    }
    player.stop();

    await sleep(1000);
    window.gc();
    const newMemory = window.performance.memory;
    const heapDifference = newMemory.usedJSHeapSize -
                           initialMemory.usedJSHeapSize;

    // eslint-disable-next-line no-console
    console.log(`
      ===========================================================
      | Current heap usage (B) | ${newMemory.usedJSHeapSize}
      | Initial heap usage (B) | ${initialMemory.usedJSHeapSize}
      | Difference (B)         | ${heapDifference}
    `);
    expect(heapDifference).to.be.below(1e7);
  });
});
<commit_msg>tests: wait a little after GC in memory tests and lower expected heap size<commit_after>import { expect } from "chai";
import RxPlayer from "../../src";
import { manifestInfos } from "../contents/DASH_static_SegmentTimeline";
import textTrackInfos from "../contents/texttracks";
import imageInfos from "../contents/imagetracks";
import sleep from "../utils/sleep.js";
import waitForPlayerState, {
  waitForLoadedStateAfterLoadVideo,
} from "../utils/waitForPlayerState";

let player;

describe("Memory tests", () => {
  afterEach(() => {
    if (player != null) {
      player.dispose();
    }
  });

  it("should not have a sensible memory leak after playing a content", async function() {
    if (window.performance == null ||
        window.performance.memory == null ||
        window.gc == null)
    {
      // eslint-disable-next-line no-console
      console.warn("API not available. Skipping test.");
      return;
    }
    this.timeout(5 * 60 * 1000);
    player = new RxPlayer({ initialVideoBitrate: Infinity,
                            initialAudioBitrate: Infinity,
                            preferredTextTracks: [{ language: "fra",
                                                    closedCaption: true }] });
    window.gc();
    await sleep(100);
    const initialMemory = window.performance.memory;

    player.loadVideo({ url: manifestInfos.url,
                       transport: manifestInfos.transport,
                       supplementaryTextTracks: [{ url: textTrackInfos.url,
                                                   language: "fra",
                                                   mimeType: "application/ttml+xml",
                                                   closedCaption: true }],
                       supplementaryImageTracks: [{ mimeType: "application/bif",
                                                    url: imageInfos.url }],
                       autoPlay: true });
    player.setPlaybackRate(4);
    await waitForPlayerState(player, "ENDED");

    player.stop();
    await sleep(100);
    window.gc();
    await sleep(100);
    const newMemory = window.performance.memory;
    const heapDifference = newMemory.usedJSHeapSize -
                           initialMemory.usedJSHeapSize;

    // eslint-disable-next-line no-console
    console.log(`
      ===========================================================
      | Current heap usage (B) | ${newMemory.usedJSHeapSize}
      | Initial heap usage (B) | ${initialMemory.usedJSHeapSize}
      | Difference (B)         | ${heapDifference}
    `);
    expect(heapDifference).to.be.below(1e6);
  });

  it("should not have a sensible memory leak after 1000 LOADED states and adaptive streaming", async function() {
    if (window.performance == null ||
        window.performance.memory == null ||
        window.gc == null)
    {
      // eslint-disable-next-line no-console
      console.warn("API not available. Skipping test.");
      return;
    }
    this.timeout(5 * 60 * 1000);
    player = new RxPlayer({ initialVideoBitrate: Infinity,
                            initialaudiobitrate: Infinity,
                            preferredtexttracks: [{ language: "fra",
                                                    closedcaption: true }] });
    window.gc();
    await sleep(100);
    const initialMemory = window.performance.memory;

    for (let i = 0; i < 1000; i++) {
      player.loadVideo({ url: manifestInfos.url,
                         transport: manifestInfos.transport,
                         supplementaryTextTracks: [{ url: textTrackInfos.url,
                                                     language: "fra",
                                                     mimeType: "application/ttml+xml",
                                                     closedCaption: true }],
                         supplementaryImageTracks: [{ mimeType: "application/bif",
                                                      url: imageInfos.url }],
                         autoPlay: true });
      await waitForLoadedStateAfterLoadVideo(player);
    }
    player.stop();

    await sleep(100);
    window.gc();
    await sleep(100);
    const newMemory = window.performance.memory;
    const heapDifference = newMemory.usedJSHeapSize -
                           initialMemory.usedJSHeapSize;

    // eslint-disable-next-line no-console
    console.log(`
      ===========================================================
      | Current heap usage (B) | ${newMemory.usedJSHeapSize}
      | Initial heap usage (B) | ${initialMemory.usedJSHeapSize}
      | Difference (B)         | ${heapDifference}
    `);
    expect(heapDifference).to.be.below(2e6);
  });
});
<|endoftext|>


<commit_before>var cacheCollection = require('./cacheCollection');

/**
 * cacheService constructor
 * @constructor
 * @param cacheServiceConfig: {
 *   nameSpace:               {string | ''},
 *   verbose:                 {boolean | false},
 *   writeToVolatileCaches:   {boolean | true}
 * }
 * @param cacheModules: [
 *    {cache module object}
 * ]
 */
function cacheService(cacheServiceConfig, cacheModules) {
  var self = this;

  /**
   ******************************************* PUBLIC FUNCTIONS *******************************************
   */

  /**
   * Get the value associated with a given key
   * @param {string} key
   * @param {function} cb
   */
  self.get = function(key, cb){
    if(arguments.length < 2){
      throw new exception('INCORRECT_ARGUMENT_EXCEPTION', '.get() requires 2 arguments.');
    }
    log(false, '.get() called:', {key: key});
    var curCache;
    var curCacheIndex = 0;
    var callback = function(err, result){
      var status = checkCacheResponse(key, err, result, curCache.type, curCacheIndex - 1);
      if(status.status === 'continue'){
        if(status.toIndex){
          curCacheIndex = status.toIndex;
        }
        getNextCache();
        return;
      }
      if(status.status === 'break'){
        cb(err, status.result);
        if(self.writeToVolatileCaches){
          writeToVolatileCaches(curCacheIndex - 1, key, result);
        }
      }
      else {
        log(false, '.get() key not found:', {key: key});
        cb(null, null);
      }
    }
    function getNextCache(){
      if(curCacheIndex < self.caches.length){
        curCache = self.caches[curCacheIndex++];
        curCache.get(key, callback);
      }
    }
    getNextCache();
  }

  /**
   * Get multiple values given multiple keys
   * @param {array} keys
   * @param {function} cb
   */
  self.mget = function(keys, cb){
    if(arguments.length < 2){
      throw new exception('INCORRECT_ARGUMENT_EXCEPTION', '.mget() requires 2 arguments.');
    }
    log(false, '.mget() called:', {keys: keys});
    var maxKeysFound = 0;
    var returnError = null;
    var returnResponse = null;
    var keepGoing = true;
    var i = 0;
    var successIndex = 0;
    var finished = false;
    var finish = function(index){
      if(!finished){
        finished = true;
        keepGoing = false;
        if(self.writeToVolatileCaches){
          writeToVolatileCaches(index, returnResponse);
        }
        cb(returnError, returnResponse);
      }
    }
    var callback = function(err, response, index){
      var objectSize = 0;
      for(key in response){
        if(response.hasOwnProperty(key)){
          ++objectSize;
        }
      }
      if(objectSize === keys.length){
        returnResponse = response;
        finish(index);
      }
      else if(objectSize > maxKeysFound){
        maxKeysFound = objectSize;
        returnResponse = response;
        successIndex = index;
      }
      if(index + 1 === self.caches.length){
        returnResponse = response;
        finish(successIndex);
      }
    }
    while(keepGoing && i < self.caches.length){
      var cache = self.caches[i];
      cache.mget(keys, callback, i);
      i++;
    }
  }

  /**
   * Associate a key and value and optionally set an expiration
   * @param {string} key
   * @param {string | object} value
   * @param {integer} expiration
   * @param {function} refresh
   * @param {function} cb
   */
  self.set = function(){
    if(arguments.length < 2){
      throw new exception('INCORRECT_ARGUMENT_EXCEPTION', '.set() requires a minimum of 2 arguments.');
    }
    var key = arguments[0];
    var value = arguments[1];
    var expiration = arguments[2] || null;
    var refresh = (arguments.length == 5) ? arguments[3] : null;
    var cb = (arguments.length == 5) ? arguments[4] : arguments[3];
    cb = cb || noop;
    log(false, '.set() called:', {key: key, value: value});
    for(var i = 0; i < self.caches.length; i++){
      var cache = self.caches[i];
      var ref = (i == self.caches.length - 1) ? refresh : null;
      var func = (i == 0) ? cb : noop;
      cache.set(key, value, expiration, ref, func);
    }
  }

  /**
   * Associate multiple keys with multiple values and optionally set expirations per function and/or key
   * @param {object} arguments[0]
   * @param {integer} arguments[1]
   * @param {function} arguments[2]
   */
  self.mset = function(){
    if(arguments.length < 1){
      throw new exception('INCORRECT_ARGUMENT_EXCEPTION', '.mset() requires a minimum of 1 argument.');
    }
    var obj = arguments[0];
    var expiration = arguments[1] || null;
    var cb = arguments[2] || null;
    log(false, '.mset() called:', {data: obj});
    for(var i = 0; i < self.caches.length; i++){
      var cache = self.caches[i];
      expiration = expiration || cache.expiration;
      if(i === self.caches.length - 1){
        cache.mset(obj, expiration, cb);
      }
      else{
        cache.mset(obj, expiration);
      }
    }
  }

  /**
   * Delete a list of keys and their values
   * @param {array} keys
   * @param {function} cb
   */
  self.del = function(keys, cb){
    if(arguments.length < 1){
      throw new exception('INCORRECT_ARGUMENT_EXCEPTION', '.del() requires a minimum of 1 argument.');
    }
    log(false, '.del() called:', {keys: keys});
    for(var i = 0; i < self.caches.length; i++){
      var cache = self.caches[i];
      if(i === self.caches.length - 1){
        cache.del(keys, cb);
      }
      else{
        cache.del(keys);
      }
    }
  }

  /**
   * Flush all keys and values from all configured caches in caches
   * @param {function} cb
   */
  self.flush = function(cb){
    log(false, '.flush() called');
    for(var i = 0; i < self.caches.length; i++){
      var cache = self.caches[i];
      if(i === self.caches.length - 1){
        cache.flush(cb);
      }
      else{
        cache.flush();
      }
    }
  }

  /**
   ******************************************* PRIVATE FUNCTIONS *******************************************
   */

  /**
   * Initialize cacheService given the provided constructor params
   */
  function init(){
    if(!cacheServiceConfig) cacheServiceConfig = {};
    self.nameSpace = cacheServiceConfig.nameSpace || '';
    self.verbose = (typeof cacheServiceConfig.verbose === 'boolean') ? cacheServiceConfig.verbose : false;
    self.writeToVolatileCaches = (typeof cacheServiceConfig.writeToVolatileCaches === 'boolean') ? cacheServiceConfig.writeToVolatileCaches : true;
    self.caches = new cacheCollection({nameSpace: self.nameSpace, verbose: self.verbose}, cacheModules).caches;
  }

  /**
   * Decides what action cacheService should take given the response from a configured cache
   * @param {string} key
   * @param {null | object} err
   * @param {null | object | string} result
   * @param {string} type
   * @param {integer} cacheIndex
   */
  function checkCacheResponse(key, err, result, type, cacheIndex){
    if(err){
      log(true, 'Error when getting key ' + key + ' from cache of type ' + type + ':', err);
      if(i < self.caches.length - 1){
        return {status:'continue'};
      }
    }
    //THIS ALLOWS false AS A VALID CACHE VALUE, BUT DO I WANT null TO BE VALID AS WELL?
    if(result !== null && typeof result !== 'undefined'){
      log(false, 'Key found:', {key: key, value: result});
      return {status: 'break', result: result};
    }
    var curCache = self.caches[cacheIndex];
    for(var i = cacheIndex + 1; i < self.caches.length; i++){
      var nextCache = self.caches[i];
      if(nextCache.checkOnPreviousEmpty || nextCache.expiration > curCache.expiration){
        return {status:'continue', toIndex: i};
      }
    }
    return {status: 'else'};
  }

  /**
   * Writes data to caches that appear before the current cache in caches
   * @param {integer} currentCacheIndex
   * @param {string} key
   * @param {null | object | string} value
   */
  function writeToVolatileCaches(currentCacheIndex, key, value){
    if(currentCacheIndex > 0){
      var curExpiration = self.caches[currentCacheIndex].expiration;
      for(var tempIndex = currentCacheIndex; tempIndex > -1; tempIndex--){
        var preExpiration = self.caches[tempIndex].expiration;
        if(preExpiration <= curExpiration){
          var preCache = self.caches[currentCacheIndex];
          if(value){
            preCache.set(key, value); /*This means that a more volatile cache can have a key longer than a less volatile cache. Should I adjust this?*/
          }
          else if(typeof key === 'object'){
            preCache.mset(key);
          }
        }
      }
    }
  }

  /**
   * Instantates an exception to be thrown
   * @param {string} name
   * @param {string} message
   * @return {exception}
   */
  function exception(name, message){
    this.name = name;
    this.message = message;
  }

  /**
   * Logging utility function
   * @param {boolean} isError
   * @param {string} message
   * @param {object} data
   */
  function log(isError, message, data){
    var indentifier = 'cacheService: ';
    if(self.verbose || isError){
      if(data) console.log(indentifier + message, data);
      else console.log(indentifier + message);
    }
  }

  function noop(){}

  init();
}

module.exports = cacheService;
<commit_msg>Lint cacheservice module<commit_after>var cacheCollection = require('./cacheCollection');

/**
 * cacheService constructor
 * @constructor
 * @param cacheServiceConfig: {
 *   nameSpace:               {string | ''},
 *   verbose:                 {boolean | false},
 *   writeToVolatileCaches:   {boolean | true}
 * }
 * @param cacheModules: [
 *    {cache module object}
 * ]
 */
function cacheService(cacheServiceConfig, cacheModules) {
  var self = this;

  /**
   ******************************************* PUBLIC FUNCTIONS *******************************************
   */

  /**
   * Get the value associated with a given key
   * @param {string} key
   * @param {function} cb
   */
  self.get = function(key, cb){
    if(arguments.length < 2){
      throw new Exception('INCORRECT_ARGUMENT_EXCEPTION', '.get() requires 2 arguments.');
    }
    log(false, '.get() called:', {key: key});
    var curCache;
    var curCacheIndex = 0;
    var callback = function(err, result){
      var status = checkCacheResponse(key, err, result, curCache.type, curCacheIndex - 1);
      if(status.status === 'continue'){
        if(status.toIndex){
          curCacheIndex = status.toIndex;
        }
        getNextCache();
        return;
      }
      if(status.status === 'break'){
        cb(err, status.result);
        if(self.writeToVolatileCaches){
          writeToVolatileCaches(curCacheIndex - 1, key, result);
        }
      }
      else {
        log(false, '.get() key not found:', {key: key});
        cb(null, null);
      }
    };
    function getNextCache(){
      if(curCacheIndex < self.caches.length){
        curCache = self.caches[curCacheIndex++];
        curCache.get(key, callback);
      }
    }
    getNextCache();
  };

  /**
   * Get multiple values given multiple keys
   * @param {array} keys
   * @param {function} cb
   */
  self.mget = function(keys, cb){
    if(arguments.length < 2){
      throw new Exception('INCORRECT_ARGUMENT_EXCEPTION', '.mget() requires 2 arguments.');
    }
    log(false, '.mget() called:', {keys: keys});
    var maxKeysFound = 0;
    var returnError = null;
    var returnResponse = null;
    var keepGoing = true;
    var i = 0;
    var successIndex = 0;
    var finished = false;
    var finish = function(index){
      if(!finished){
        finished = true;
        keepGoing = false;
        if(self.writeToVolatileCaches){
          writeToVolatileCaches(index, returnResponse);
        }
        cb(returnError, returnResponse);
      }
    };
    var callback = function(err, response, index){
      var objectSize = 0;
      for (var key in response){
        if(response.hasOwnProperty(key)){
          ++objectSize;
        }
      }
      if(objectSize === keys.length){
        returnResponse = response;
        finish(index);
      }
      else if(objectSize > maxKeysFound){
        maxKeysFound = objectSize;
        returnResponse = response;
        successIndex = index;
      }
      if(index + 1 === self.caches.length){
        returnResponse = response;
        finish(successIndex);
      }
    };
    while(keepGoing && i < self.caches.length){
      var cache = self.caches[i];
      cache.mget(keys, callback, i);
      i++;
    }
  };

  /**
   * Associate a key and value and optionally set an expiration
   * @param {string} key
   * @param {string | object} value
   * @param {integer} expiration
   * @param {function} refresh
   * @param {function} cb
   */
  self.set = function(){
    if(arguments.length < 2){
      throw new Exception('INCORRECT_ARGUMENT_EXCEPTION', '.set() requires a minimum of 2 arguments.');
    }
    var key = arguments[0];
    var value = arguments[1];
    var expiration = arguments[2] || null;
    var refresh = (arguments.length === 5) ? arguments[3] : null;
    var cb = (arguments.length === 5) ? arguments[4] : arguments[3];
    cb = cb || noop;
    log(false, '.set() called:', {key: key, value: value});
    for(var i = 0; i < self.caches.length; i++){
      var cache = self.caches[i];
      var ref = (i === self.caches.length - 1) ? refresh : null;
      var func = (i === 0) ? cb : noop;
      cache.set(key, value, expiration, ref, func);
    }
  };

  /**
   * Associate multiple keys with multiple values and optionally set expirations per function and/or key
   * @param {object} arguments[0]
   * @param {integer} arguments[1]
   * @param {function} arguments[2]
   */
  self.mset = function(){
    if(arguments.length < 1){
      throw new Exception('INCORRECT_ARGUMENT_EXCEPTION', '.mset() requires a minimum of 1 argument.');
    }
    var obj = arguments[0];
    var expiration = arguments[1] || null;
    var cb = arguments[2] || null;
    log(false, '.mset() called:', {data: obj});
    for(var i = 0; i < self.caches.length; i++){
      var cache = self.caches[i];
      expiration = expiration || cache.expiration;
      if(i === self.caches.length - 1){
        cache.mset(obj, expiration, cb);
      }
      else{
        cache.mset(obj, expiration);
      }
    }
  };

  /**
   * Delete a list of keys and their values
   * @param {array} keys
   * @param {function} cb
   */
  self.del = function(keys, cb){
    if(arguments.length < 1){
      throw new Exception('INCORRECT_ARGUMENT_EXCEPTION', '.del() requires a minimum of 1 argument.');
    }
    log(false, '.del() called:', {keys: keys});
    for(var i = 0; i < self.caches.length; i++){
      var cache = self.caches[i];
      if(i === self.caches.length - 1){
        cache.del(keys, cb);
      }
      else{
        cache.del(keys);
      }
    }
  };

  /**
   * Flush all keys and values from all configured caches in caches
   * @param {function} cb
   */
  self.flush = function(cb){
    log(false, '.flush() called');
    for(var i = 0; i < self.caches.length; i++){
      var cache = self.caches[i];
      if(i === self.caches.length - 1){
        cache.flush(cb);
      }
      else{
        cache.flush();
      }
    }
  };

  /**
   ******************************************* PRIVATE FUNCTIONS *******************************************
   */

  /**
   * Initialize cacheService given the provided constructor params
   */
  function init(){
    if(!cacheServiceConfig) {
      cacheServiceConfig = {};
    }
    self.nameSpace = cacheServiceConfig.nameSpace || '';
    self.verbose = (typeof cacheServiceConfig.verbose === 'boolean') ? cacheServiceConfig.verbose : false;
    self.writeToVolatileCaches = (typeof cacheServiceConfig.writeToVolatileCaches === 'boolean') ? cacheServiceConfig.writeToVolatileCaches : true;
    self.caches = new cacheCollection({nameSpace: self.nameSpace, verbose: self.verbose}, cacheModules).caches;
  }

  /**
   * Decides what action cacheService should take given the response from a configured cache
   * @param {string} key
   * @param {null | object} err
   * @param {null | object | string} result
   * @param {string} type
   * @param {integer} cacheIndex
   */
  function checkCacheResponse(key, err, result, type, cacheIndex){
    if(err){
      log(true, 'Error when getting key ' + key + ' from cache of type ' + type + ':', err);
      if(i < self.caches.length - 1){
        return {status:'continue'};
      }
    }
    //THIS ALLOWS false AS A VALID CACHE VALUE, BUT DO I WANT null TO BE VALID AS WELL?
    if(result !== null && typeof result !== 'undefined'){
      log(false, 'Key found:', {key: key, value: result});
      return {status: 'break', result: result};
    }
    var curCache = self.caches[cacheIndex];
    for(var i = cacheIndex + 1; i < self.caches.length; i++) {
      var nextCache = self.caches[i];
      if(nextCache.checkOnPreviousEmpty || nextCache.expiration > curCache.expiration){
        return {status:'continue', toIndex: i};
      }
    }
    return {status: 'else'};
  }

  /**
   * Writes data to caches that appear before the current cache in caches
   * @param {integer} currentCacheIndex
   * @param {string} key
   * @param {null | object | string} value
   */
  function writeToVolatileCaches(currentCacheIndex, key, value){
    if(currentCacheIndex > 0){
      var curExpiration = self.caches[currentCacheIndex].expiration;
      for(var tempIndex = currentCacheIndex; tempIndex > -1; tempIndex--){
        var preExpiration = self.caches[tempIndex].expiration;
        if(preExpiration <= curExpiration){
          var preCache = self.caches[currentCacheIndex];
          if(value){
            preCache.set(key, value); /*This means that a more volatile cache can have a key longer than a less volatile cache. Should I adjust this?*/
          }
          else if(typeof key === 'object'){
            preCache.mset(key);
          }
        }
      }
    }
  }

  /**
   * Instantates an exception to be thrown
   * @param {string} name
   * @param {string} message
   * @return {exception}
   */
  function Exception(name, message) {
    this.name = name;
    this.message = message;
  }

  /**
   * Logging utility function
   * @param {boolean} isError
   * @param {string} message
   * @param {object} data
   */
  function log(isError, message, data){
    var indentifier = 'cacheService: ';
    if(self.verbose || isError){
      if(data) {
        console.log(indentifier + message, data);
      } else {
        console.log(indentifier + message);
      }
    }
  }

  function noop(){}

  init();
}

module.exports = cacheService;
<|endoftext|>


<commit_before>/**
 * Created by CristoH on 21/04/2016.
 */
Meteor.publish('userProfileInfo', function(userId){

    if(userId && this.userId){

        var user = Meteor.users.findOne({_id: userId});

        if(user.services){
            if(user.services.twitter && !user.profile.display_picture && !user.username){
                console.log('Actualizando usuario con twitter');
                Meteor.users.update({_id: userId},{$set: { profile: { display_picture: user.services.twitter.profile_image_url }, username: user.services.twitter.screenName}});
                //user.profile.display_picture = user.services.twitter.profile_image_url;
                //user.username = user.services.twitter.screenName;
            }
            if(user.services.facebook && !user.profile.firstName && !user.profile.lastName && !user.username){
                console.log('Actualizando usuario con facebook');
                Meteor.users.update({_id: userId},{$set: { profile :{ firstName: user.services.facebook.first_name, lastName: user.services.facebook.last_name }, username: user.services.facebook.first_name}});
            }
        }
        return Meteor.users.find({_id: userId}, {fields: {
            username:1,
            profile:1,
            followers:1,
            followersCount:1,
            following:1,
            followingCount:1,
            postsCount: 1,
            "services.twitter": 1,
            "services.facebook": 1
        }});
    }else{
        this.ready();
    }
});
<commit_msg>Update user services<commit_after>/**
 * Created by CristoH on 21/04/2016.
 */
Meteor.publish('userProfileInfo', function(userId){

    if(userId && this.userId){

        var user = Meteor.users.findOne({_id: userId});

        if(user.services){
            if(user.services.twitter && !user.profile){
                console.log('Actualizando usuario con twitter');
                Meteor.users.update({_id: userId},{$set: { profile: { display_picture: user.services.twitter.profile_image_url }, username: user.services.twitter.screenName}});
                //user.profile.display_picture = user.services.twitter.profile_image_url;
                //user.username = user.services.twitter.screenName;
            }
            if(user.services.facebook && !user.profile){
                console.log('Actualizando usuario con facebook');
                Meteor.users.update({_id: userId},{$set: { profile :{ firstName: user.services.facebook.first_name, lastName: user.services.facebook.last_name }, username: user.services.facebook.first_name}});
            }
        }
        return Meteor.users.find({_id: userId}, {fields: {
            username:1,
            profile:1,
            followers:1,
            followersCount:1,
            following:1,
            followingCount:1,
            postsCount: 1,
            "services.twitter": 1,
            "services.facebook": 1
        }});
    }else{
        this.ready();
    }
});
<|endoftext|>


<commit_before>'use strict';

class ImgView extends HTMLElement {

    static get is () {
        return 'img-view';
    }

    /* 画像URIを与える */
    set src (val) {
        this.setAttribute('src', val);
    }

    get src () {
        return this.getAttribute('src');
    }

    /* 背景色を与える */
    set bgcolor (val) {
        this.setAttribute('bgcolor', val);
    }

    get bgcolor () {
        return this.getAttribute('bgcolor');
    }

    /* 角丸長方形（あるいは円形）指定をする */
    set radius (val) {
        this.setAttribute('radius', val);
    }

    get radius () {
        return this.getAttribute('radius');
    }

    createdCallback () {
        this.createShadowRoot().innerHTML = `

        `;
        this.updateWebView();
    }

    updateWebView () {
        var g = this.shadowRoot;
        var base = 'img-webview.html?';
        if (this.src !== undefined) {
            var width = this.offsetWidth + 'px';
            var height = this.offsetHeight + 'px';
            var bgcolor = this.bgcolor || '#fff';
            var src = this.src || '#';
            var radius = this.radius || '0px';
            var query = 'src=' + src + '&w=' + width + '&h=' + height + '&c=' + bgcolor + '&r=' + radius;

            var webview = "<webview class='imgview-webview' partition='static' src='"+ (base + query) +"'></webview>";
            var layer = "

";
            console.info(base + query)
            console.info(width, height, bgcolor, radius);

            g.querySelector('.imgview-root').innerHTML = '';
            g.querySelector('.imgview-root').innerHTML = (webview + layer);
        }
    }

    attachedCallback () {
    }

    detachedCallback () {
    }

    attributeChangedCallback (e) {
        console.info(e)
    }
}

document.registerElement(ImgView.is, ImgView);
<commit_msg>簡単に書けるところは簡単に<commit_after>'use strict';

class ImgView extends HTMLElement {

    static get is () {
        return 'img-view';
    }

    /* 画像URIを与える */
    set src (val) {
        this.setAttribute('src', val);
    }

    get src () {
        return this.getAttribute('src');
    }

    /* 背景色を与える */
    set bgcolor (val) {
        this.setAttribute('bgcolor', val);
    }

    get bgcolor () {
        return this.getAttribute('bgcolor');
    }

    /* 角丸長方形（あるいは円形）指定をする */
    set radius (val) {
        this.setAttribute('radius', val);
    }

    get radius () {
        return this.getAttribute('radius');
    }

    createdCallback () {
        this.createShadowRoot().innerHTML = `

                <webview class='imgview-webview' partition='static' src=''></webview>

        `;
        this.updateWebView();
    }

    updateWebView () {
        var g = this.shadowRoot;
        var base = 'i.html?';
        if (this.src !== undefined) {
            var width = this.offsetWidth + 'px';
            var height = this.offsetHeight + 'px';
            var bgcolor = this.bgcolor || '#fff';
            var src = this.src || '#';
            var radius = this.radius || '0px';
            var query = 'src=' + src + '&w=' + width + '&h=' + height + '&c=' + bgcolor + '&r=' + radius;
            g.querySelector('.imgview-webview').setAttribute('src', base + query);
        }
    }

    attachedCallback () {
    }

    detachedCallback () {
    }

    attributeChangedCallback (e) {
        console.info(e)
    }
}

document.registerElement(ImgView.is, ImgView);
<|endoftext|>


<commit_before>function Alarm(hour, minute, days, active) {
  this.hour = hour;
  this.minute = minute;
  this.days = days;
  this.active = active;
  this.time = new Date(0);
  this.time.setHours(this.hour);
  this.time.setMinutes(this.minute);
  this.editing = false;
}
(function() {
  this.toggle = function() {
    this.active = !(this.active);
    console.log(this.active);
    this.pushChanges("toggle");
  };
  this.updateStartTime = function() {
    if (this.minute < 30) {
      this.start_minute = this.minute + 30;
      this.start_hour = (this.hour === 0) ? 23 : this.hour - 1;
    } else {
      this.start_minute = this.minute - 30;
      this.start_hour = this.hour;
    }
  };
  this.updateDaysTf = function () {
    this.days_tf = [];
    var j = 0;
    for (var i = 0; i < 7; i++) {
      if (this.days[j] - 1 === i) {
        this.days_tf[i] = true;
        j++;
      } else {
        this.days_tf[i] = false;
      }
    }
  };
  this.updateDays = function () {
    this.days = [];
    for (var i = 0; i < 7; i++) {
      if (this.days_tf[i] === true) {
        this.days.push(i + 1);
      }
    }
  }
  this.edit = function() {
    this.updateDaysTf();
    this.old_alarm = {
      hour: this.hour,
      minute: this.minute,
      days: this.days
    };
    this.time = this.hour + ":" + this.minute + ":00";
    this.editing = true;
  };
  this.saveEdits = function() {
    this.hour = this.time.getHours();
    this.minute = this.time.getMinutes();
    this.updateDays();
    if (isNaN(this.hour) || this.hour < 0 || this.hour > 23) {
      alert("Error. The alarm hour must be a number between 0 and 23.");
    } else if (isNaN(this.minute) || this.minute < 0 || this.minute > 59) {
      alert("Error. The alarm minute must be a number between 0 and 59.");
    } else if (this.days == []) {
      alert("Error. No days were selected.");
    } else {
      this.editing = false;
      this.active = true;
      this.updateStartTime();
      console.log(this);
      this.pushChanges("edit");
    }
  };
  this.cancelEdits = function() {
    this.editing = false;
    this.hour = this.oldAlarm.hour;
    this.minute = this.oldAlarm.minute;
    this.days = this.oldAlarm.days;
  };
  this.delete = function() {
    this.old_alarm = {
      hour: this.hour,
      minute: this.minute,
      days: this.days
    };
    this.pushChanges("delete");
    return this;
  }
  this.pushChanges = function(type) {
    var data = {
      type: type,
      alarm: JSON.stringify(this),
      authkey: authkey
    };
    $.post("saveAlarm.php", data, function (response) {
      if (response == "OK") {
        alert("The alarm was successfully updated.");
      } else {
        alert("FAIL " + response);
      }
    });
  }
}).call(Alarm.prototype);
<commit_msg>Fixed issue alarm did not update when time unchanged<commit_after>function Alarm(hour, minute, days, active) {
  this.hour = hour;
  this.minute = minute;
  this.days = days;
  this.active = active;
  this.time = new Date(0);
  this.time.setHours(this.hour);
  this.time.setMinutes(this.minute);
  this.editing = false;
}
(function() {
  this.toggle = function() {
    this.active = !(this.active);
    console.log(this.active);
    this.pushChanges("toggle");
  };
  this.updateStartTime = function() {
    if (this.minute < 30) {
      this.start_minute = this.minute + 30;
      this.start_hour = (this.hour === 0) ? 23 : this.hour - 1;
    } else {
      this.start_minute = this.minute - 30;
      this.start_hour = this.hour;
    }
  };
  this.updateDaysTf = function () {
    this.days_tf = [];
    var j = 0;
    for (var i = 0; i < 7; i++) {
      if (this.days[j] - 1 === i) {
        this.days_tf[i] = true;
        j++;
      } else {
        this.days_tf[i] = false;
      }
    }
  };
  this.updateDays = function () {
    this.days = [];
    for (var i = 0; i < 7; i++) {
      if (this.days_tf[i] === true) {
        this.days.push(i + 1);
      }
    }
  }
  this.edit = function() {
    this.updateDaysTf();
    this.old_alarm = {
      hour: this.hour,
      minute: this.minute,
      days: this.days
    };
    this.time = this.hour + ":" + this.minute + ":00";
    this.editing = true;
  };
  this.saveEdits = function() {
    if (this.hour != this.old_alarm.hour || this.minute != this.old_alarm.minute) {
      this.hour = this.time.getHours();
      this.minute = this.time.getMinutes();
    }
    this.updateDays();
    if (isNaN(this.hour) || this.hour < 0 || this.hour > 23) {
      alert("Error. The alarm hour must be a number between 0 and 23.");
    } else if (isNaN(this.minute) || this.minute < 0 || this.minute > 59) {
      alert("Error. The alarm minute must be a number between 0 and 59.");
    } else if (this.days == []) {
      alert("Error. No days were selected.");
    } else {
      this.editing = false;
      this.active = true;
      this.updateStartTime();
      console.log(this);
      this.pushChanges("edit");
    }
  };
  this.cancelEdits = function() {
    this.editing = false;
    this.hour = this.oldAlarm.hour;
    this.minute = this.oldAlarm.minute;
    this.days = this.oldAlarm.days;
  };
  this.delete = function() {
    this.old_alarm = {
      hour: this.hour,
      minute: this.minute,
      days: this.days
    };
    this.pushChanges("delete");
    return this;
  }
  this.pushChanges = function(type) {
    var data = {
      type: type,
      alarm: JSON.stringify(this),
      authkey: authkey
    };
    $.post("saveAlarm.php", data, function (response) {
      if (response == "OK") {
        alert("The alarm was successfully updated.");
      } else {
        alert("FAIL " + response);
      }
    });
  }
}).call(Alarm.prototype);
<|endoftext|>


<commit_before>import { zipSync, strToU8 } from '../libs/fflate.module.min.js';

class USDZExporter {

	async parse( scene ) {

		let output = buildHeader();

		const materials = {};
		const textures = {};

		scene.traverse( ( object ) => {

			if ( object.isMesh ) {

				const geometry = object.geometry;
				const material = object.material;

				materials[ material.uuid ] = material;

				if ( material.map !== null ) textures[ material.map.uuid ] = material.map;
				if ( material.normalMap !== null ) textures[ material.normalMap.uuid ] = material.normalMap;
				if ( material.aoMap !== null ) textures[ material.aoMap.uuid ] = material.aoMap;
				if ( material.roughnessMap !== null ) textures[ material.roughnessMap.uuid ] = material.roughnessMap;
				if ( material.metalnessMap !== null ) textures[ material.metalnessMap.uuid ] = material.metalnessMap;
				if ( material.emissiveMap !== null ) textures[ material.emissiveMap.uuid ] = material.emissiveMap;

				output += buildXform( object, buildMesh( geometry, material ) );

			}

		} );

		output += buildMaterials( materials );
		output += buildTextures( textures );

		const files = {};

		for ( const uuid in textures ) {

			const texture = textures[ uuid ];
			files[ 'Texture_' + texture.id + '.jpg' ] = await imgToU8( texture.image );

		}

		return zipSync( { 'model.usda': strToU8( output ), 'textures': files }, { level: 0 } );

	}

}

async function imgToU8( image ) {

	if ( ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) ||
		( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement ) ||
		( typeof OffscreenCanvas !== 'undefined' && image instanceof OffscreenCanvas ) ||
		( typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap ) ) {

		const canvas = document.createElement( 'canvas' );
		canvas.width = image.width;
		canvas.height = image.height;

		const context = canvas.getContext( '2d' );
		context.translate( 0, canvas.height );
		context.scale( 1, - 1 );
		context.drawImage( image, 0, 0, canvas.width, canvas.height );

		const blob = await new Promise( resolve => canvas.toBlob( resolve, 'image/jpeg' ) );
		return new Uint8Array( await blob.arrayBuffer() );

	}

}

//

function buildHeader() {

	return `#usda 1.0
(
    doc = "Three.js"
    metersPerUnit = 1
    upAxis = "Y"
)

`;

}

// Xform

function buildXform( object, define ) {

	const name = 'Object_' + object.id;
	const transform = buildMatrix( object.matrixWorld );

	return `def Xform "${ name }"
{
    matrix4d xformOp:transform = ${ transform }
    uniform token[] xformOpOrder = ["xformOp:transform"]

    ${ define }
}

`;

}

function buildMatrix( matrix ) {

	const array = matrix.elements;

	return `( ${ buildMatrixRow( array, 0 ) }, ${ buildMatrixRow( array, 4 ) }, ${ buildMatrixRow( array, 8 ) }, ${ buildMatrixRow( array, 12 ) } )`;

}

function buildMatrixRow( array, offset ) {

	return `(${ array[ offset + 0 ] }, ${ array[ offset + 1 ] }, ${ array[ offset + 2 ] }, ${ array[ offset + 3 ] })`;

}

// Mesh

function buildMesh( geometry, material ) {

	const name = 'Geometry_' + geometry.id;
	const attributes = geometry.attributes;
	const count = attributes.position.count;

	return `def Mesh "${ name }"
    {
        int[] faceVertexCounts = [${ buildMeshVertexCount( geometry ) }]
        int[] faceVertexIndices = [${ buildMeshVertexIndices( geometry ) }]
        rel material:binding = </Materials/Material_${ material.id }>
        normal3f[] normals = [${ buildVector3Array( attributes.normal, count )}] (
            interpolation = "vertex"
        )
        point3f[] points = [${ buildVector3Array( attributes.position, count )}]
        float2[] primvars:st = [${ buildVector2Array( attributes.uv, count )}] (
            elementSize = 1
            interpolation = "vertex"
        )
        int[] primvars:st:indices
        uniform token subdivisionScheme = "none"
    }
`;

}

function buildMeshVertexCount( geometry ) {

	const count = geometry.index !== null ? geometry.index.array.length : geometry.attributes.position.count;

	return Array( count / 3 ).fill( 3 ).join( ', ' );

}

function buildMeshVertexIndices( geometry ) {

	if ( geometry.index !== null ) {

		return geometry.index.array.join( ', ' );

	}

	const array = [];
	const length = geometry.attributes.position.count;

	for ( let i = 0; i < length; i ++ ) {

		array.push( i );

	}

	return array.join( ', ' );

}

function buildVector3Array( attribute, count ) {

	if ( attribute === undefined ) {

		console.warn( 'USDZExporter: Normals missing.' );
		return Array( count ).fill( '(0, 0, 0)' ).join( ', ' );

	}

	const array = [];
	const data = attribute.array;

	for ( let i = 0; i < data.length; i += 3 ) {

		array.push( `(${ data[ i + 0 ] }, ${ data[ i + 1 ] }, ${ data[ i + 2 ] })` );

	}

	return array.join( ', ' );

}

function buildVector2Array( attribute, count ) {

	if ( attribute === undefined ) {

		console.warn( 'USDZExporter: UVs missing.' );
		return Array( count ).fill( '(0, 0)' ).join( ', ' );

	}

	const array = [];
	const data = attribute.array;

	for ( let i = 0; i < data.length; i += 2 ) {

		array.push( `(${ data[ i + 0 ] }, ${ data[ i + 1 ] })` );

	}

	return array.join( ', ' );

}

// Materials

function buildMaterials( materials ) {

	const array = [];

	for ( const uuid in materials ) {

		const material = materials[ uuid ];

		array.push( buildMaterial( material ) );

	}

	return `def "Materials"
{
${ array.join( '' ) }
}

`;

}

function buildMaterial( material ) {

	const pad = '            ';
	const parameters = [];

	if ( material.map !== null ) {

		parameters.push( `${ pad }float3 inputs:diffuseColor.connect = </Textures/Texture_${ material.map.id }.outputs:rgb>` );

	} else {

		parameters.push( `${ pad }float3 inputs:diffuseColor = ${ buildColor( material.color ) }` );

	}

	if ( material.normalMap !== null ) {

		parameters.push( `${ pad }float3 inputs:normal.connect = </Textures/Texture_${ material.normalMap.id }.outputs:rgb>` );

	}

	if ( material.aoMap !== null ) {

		parameters.push( `${ pad }float inputs:occlusion.connect = </Textures/Texture_${ material.aoMap.id }.outputs:rgb>` );

	}

	if ( material.roughnessMap !== null ) {

		parameters.push( `${ pad }float inputs:roughness.connect = </Textures/Texture_${ material.roughnessMap.id }.outputs:rgb>` );

	} else {

		parameters.push( `${ pad }float inputs:roughness = ${ material.roughness }` );

	}

	if ( material.metalnessMap !== null ) {

		parameters.push( `${ pad }float inputs:metalness.connect = </Textures/Texture_${ material.metalnessMap.id }.outputs:rgb>` );

	} else {

		parameters.push( `${ pad }float inputs:metallic = ${ material.metalness }` );

	}

	if ( material.emissiveMap !== null ) {

		parameters.push( `${ pad }float3 inputs:emissive.connect = </Textures/Texture_${ material.emissiveMap.id }.outputs:rgb>` );

	}

	return `
    def Material "Material_${ material.id }"
    {
        token outputs:surface.connect = </Materials/Material_${ material.id }/PreviewSurface.outputs:surface>

        def Shader "PreviewSurface"
        {
            uniform token info:id = "UsdPreviewSurface"
${ parameters.join( '\n' ) }
            int inputs:useSpecularWorkflow = 0
            token outputs:surface
        }
    }
`;

}

function buildTextures( textures ) {

	const array = [];

	for ( const uuid in textures ) {

		const texture = textures[ uuid ];

		array.push( buildTexture( texture ) );

	}

	return `def "Textures"
{
${ array.join( '' ) }
}

`;

}

function buildTexture( texture ) {

	return `
    def Shader "Texture_${ texture.id }"
    {
        uniform token info:id = "UsdUVTexture"
        asset inputs:file = @textures/Texture_${ texture.id }.jpg@
        token inputs:isSRGB = "auto"
        token inputs:wrapS = "repeat"
        token inputs:wrapT = "repeat"
        float3 outputs:rgb
    }
`;

}

function buildColor( color ) {

	return `(${ color.r }, ${ color.g }, ${ color.b })`;

}

export { USDZExporter };
<commit_msg>USDZExporter: Flip uvs instead of textures.<commit_after>import { zipSync, strToU8 } from '../libs/fflate.module.min.js';

class USDZExporter {

	async parse( scene ) {

		let output = buildHeader();

		const materials = {};
		const textures = {};

		scene.traverse( ( object ) => {

			if ( object.isMesh ) {

				const geometry = object.geometry;
				const material = object.material;

				materials[ material.uuid ] = material;

				if ( material.map !== null ) textures[ material.map.uuid ] = material.map;
				if ( material.normalMap !== null ) textures[ material.normalMap.uuid ] = material.normalMap;
				if ( material.aoMap !== null ) textures[ material.aoMap.uuid ] = material.aoMap;
				if ( material.roughnessMap !== null ) textures[ material.roughnessMap.uuid ] = material.roughnessMap;
				if ( material.metalnessMap !== null ) textures[ material.metalnessMap.uuid ] = material.metalnessMap;
				if ( material.emissiveMap !== null ) textures[ material.emissiveMap.uuid ] = material.emissiveMap;

				output += buildXform( object, buildMesh( geometry, material ) );

			}

		} );

		output += buildMaterials( materials );
		output += buildTextures( textures );

		const files = {};

		for ( const uuid in textures ) {

			const texture = textures[ uuid ];
			files[ 'Texture_' + texture.id + '.jpg' ] = await imgToU8( texture.image );

		}

		return zipSync( { 'model.usda': strToU8( output ), 'textures': files }, { level: 0 } );

	}

}

async function imgToU8( image ) {

	if ( ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) ||
		( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement ) ||
		( typeof OffscreenCanvas !== 'undefined' && image instanceof OffscreenCanvas ) ||
		( typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap ) ) {

		const canvas = document.createElement( 'canvas' );
		canvas.width = image.width;
		canvas.height = image.height;

		const context = canvas.getContext( '2d' );
		context.drawImage( image, 0, 0, canvas.width, canvas.height );

		const blob = await new Promise( resolve => canvas.toBlob( resolve, 'image/jpeg' ) );
		return new Uint8Array( await blob.arrayBuffer() );

	}

}

//

function buildHeader() {

	return `#usda 1.0
(
    doc = "Three.js"
    metersPerUnit = 1
    upAxis = "Y"
)

`;

}

// Xform

function buildXform( object, define ) {

	const name = 'Object_' + object.id;
	const transform = buildMatrix( object.matrixWorld );

	return `def Xform "${ name }"
{
    matrix4d xformOp:transform = ${ transform }
    uniform token[] xformOpOrder = ["xformOp:transform"]

    ${ define }
}

`;

}

function buildMatrix( matrix ) {

	const array = matrix.elements;

	return `( ${ buildMatrixRow( array, 0 ) }, ${ buildMatrixRow( array, 4 ) }, ${ buildMatrixRow( array, 8 ) }, ${ buildMatrixRow( array, 12 ) } )`;

}

function buildMatrixRow( array, offset ) {

	return `(${ array[ offset + 0 ] }, ${ array[ offset + 1 ] }, ${ array[ offset + 2 ] }, ${ array[ offset + 3 ] })`;

}

// Mesh

function buildMesh( geometry, material ) {

	const name = 'Geometry_' + geometry.id;
	const attributes = geometry.attributes;
	const count = attributes.position.count;

	return `def Mesh "${ name }"
    {
        int[] faceVertexCounts = [${ buildMeshVertexCount( geometry ) }]
        int[] faceVertexIndices = [${ buildMeshVertexIndices( geometry ) }]
        rel material:binding = </Materials/Material_${ material.id }>
        normal3f[] normals = [${ buildVector3Array( attributes.normal, count )}] (
            interpolation = "vertex"
        )
        point3f[] points = [${ buildVector3Array( attributes.position, count )}]
        float2[] primvars:st = [${ buildVector2Array( attributes.uv, count )}] (
            elementSize = 1
            interpolation = "vertex"
        )
        int[] primvars:st:indices
        uniform token subdivisionScheme = "none"
    }
`;

}

function buildMeshVertexCount( geometry ) {

	const count = geometry.index !== null ? geometry.index.array.length : geometry.attributes.position.count;

	return Array( count / 3 ).fill( 3 ).join( ', ' );

}

function buildMeshVertexIndices( geometry ) {

	if ( geometry.index !== null ) {

		return geometry.index.array.join( ', ' );

	}

	const array = [];
	const length = geometry.attributes.position.count;

	for ( let i = 0; i < length; i ++ ) {

		array.push( i );

	}

	return array.join( ', ' );

}

function buildVector3Array( attribute, count ) {

	if ( attribute === undefined ) {

		console.warn( 'USDZExporter: Normals missing.' );
		return Array( count ).fill( '(0, 0, 0)' ).join( ', ' );

	}

	const array = [];
	const data = attribute.array;

	for ( let i = 0; i < data.length; i += 3 ) {

		array.push( `(${ data[ i + 0 ] }, ${ data[ i + 1 ] }, ${ data[ i + 2 ] })` );

	}

	return array.join( ', ' );

}

function buildVector2Array( attribute, count ) {

	if ( attribute === undefined ) {

		console.warn( 'USDZExporter: UVs missing.' );
		return Array( count ).fill( '(0, 0)' ).join( ', ' );

	}

	const array = [];
	const data = attribute.array;

	for ( let i = 0; i < data.length; i += 2 ) {

		array.push( `(${ data[ i + 0 ] }, ${ 1 - data[ i + 1 ] })` );

	}

	return array.join( ', ' );

}

// Materials

function buildMaterials( materials ) {

	const array = [];

	for ( const uuid in materials ) {

		const material = materials[ uuid ];

		array.push( buildMaterial( material ) );

	}

	return `def "Materials"
{
${ array.join( '' ) }
}

`;

}

function buildMaterial( material ) {

	const pad = '            ';
	const parameters = [];

	if ( material.map !== null ) {

		parameters.push( `${ pad }float3 inputs:diffuseColor.connect = </Textures/Texture_${ material.map.id }.outputs:rgb>` );

	} else {

		parameters.push( `${ pad }float3 inputs:diffuseColor = ${ buildColor( material.color ) }` );

	}

	if ( material.normalMap !== null ) {

		parameters.push( `${ pad }float3 inputs:normal.connect = </Textures/Texture_${ material.normalMap.id }.outputs:rgb>` );

	}

	if ( material.aoMap !== null ) {

		parameters.push( `${ pad }float inputs:occlusion.connect = </Textures/Texture_${ material.aoMap.id }.outputs:rgb>` );

	}

	if ( material.roughnessMap !== null ) {

		parameters.push( `${ pad }float inputs:roughness.connect = </Textures/Texture_${ material.roughnessMap.id }.outputs:rgb>` );

	} else {

		parameters.push( `${ pad }float inputs:roughness = ${ material.roughness }` );

	}

	if ( material.metalnessMap !== null ) {

		parameters.push( `${ pad }float inputs:metalness.connect = </Textures/Texture_${ material.metalnessMap.id }.outputs:rgb>` );

	} else {

		parameters.push( `${ pad }float inputs:metallic = ${ material.metalness }` );

	}

	if ( material.emissiveMap !== null ) {

		parameters.push( `${ pad }float3 inputs:emissive.connect = </Textures/Texture_${ material.emissiveMap.id }.outputs:rgb>` );

	}

	return `
    def Material "Material_${ material.id }"
    {
        token outputs:surface.connect = </Materials/Material_${ material.id }/PreviewSurface.outputs:surface>

        def Shader "PreviewSurface"
        {
            uniform token info:id = "UsdPreviewSurface"
${ parameters.join( '\n' ) }
            int inputs:useSpecularWorkflow = 0
            token outputs:surface
        }
    }
`;

}

function buildTextures( textures ) {

	const array = [];

	for ( const uuid in textures ) {

		const texture = textures[ uuid ];

		array.push( buildTexture( texture ) );

	}

	return `def "Textures"
{
${ array.join( '' ) }
}

`;

}

function buildTexture( texture ) {

	return `
    def Shader "Texture_${ texture.id }"
    {
        uniform token info:id = "UsdUVTexture"
        asset inputs:file = @textures/Texture_${ texture.id }.jpg@
        token inputs:isSRGB = "auto"
        token inputs:wrapS = "repeat"
        token inputs:wrapT = "repeat"
        float3 outputs:rgb
    }
`;

}

function buildColor( color ) {

	return `(${ color.r }, ${ color.g }, ${ color.b })`;

}

export { USDZExporter };
<|endoftext|>