<commit_before>var Tuner = (function () {

  function Tuner(callback, stream) {

    if (Tuner.prototype._instance) {
      return Tuner.prototype._instance;
    }

    if (!callback) {
      throw new PropertyNotInitialized("Tuner", "callback");
    }

    Tuner.prototype._instance = this;

    var context              = new AudioContext()
      , _samplerate          = 44100
      , _downsampleFactor    = 12
      , _fftSize             = 2048
      , _effectiveSamplerate = _samplerate / _downsampleFactor
      , _frequencyResolution = _effectiveSamplerate / _fftSize
      , _bufferSize          = _fftSize * _downsampleFactor
      , _temporalWindow      = _bufferSize / _samplerate
      , _harmonics           = 5
      , _maxHarmFrequency    = _fftSize / _harmonics * _frequencyResolution
      , fft                  = new FFT(_fftSize, _effectiveSamplerate)
      , samples              = new Ring(_bufferSize, 512)
      , _windowFunction      = new WindowObject("Hann", _bufferSize)
      , _frequencyMap        = new FrequencyMap()
      , _viewCallback        = callback
      ;

    var source    = context.createMediaStreamSource(stream)
      , lowpass   = context.createBiquadFilter()
      , highpass  = context.createBiquadFilter()
      , processor = context.createScriptProcessor(512,1,1)
      ;

    lowpass.type       = "lowpass";
    highpass.type      = "highpass";
    lowpass.frequency  = (_effectiveSamplerate / 2).toFixed(3);
    highpass.frequency = 35;

    Object.defineProperties(this, {
        "samplerate" : {
            value        : _samplerate
          , enumerable   : true
          , configurable : false
          , writable     : false
        }
      , "viewCallback" : {
            value        : _viewCallback
          , enumerable   : false
          , configurable : false
          , writable     : true
        }
      , "downsampleFactor" : {
            enumerable   : true
          , configurable : false
          , get : function () {
              return _downsampleFactor;
            }
          , set : function (value) {

              checkNat("downsampleFactor",value);

              _downsampleFactor = Math.floor(value);

              _effectiveSamplerate = _samplerate / _downsampleFactor;
              _frequencyResolution = _effectiveSamplerate / _fftSize;
              _bufferSize       = _fftSize * _downsampleFactor;
              _temporalWindow   = _bufferSize / _samplerate;
              _maxHarmFrequency = _fftSize / _harmonics * _frequencyResolution;

              fft     = new FFT(_fftSize, _effectiveSamplerate);
              samples = new Ring(_bufferSize, 512);

              lowpass.frequency  = (_effectiveSamplerate / 2).toFixed(3);

              _windowFunction.length = _bufferSize;
            }
        }
      , "fftSize" : {
            enumerable   : true
          , configurable : false
          , get : function () {
              return _fftSize;
            }
          , set : function (value) {
              _fftSize = value;

              _frequencyResolution = _effectiveSamplerate / _fftSize;
              _bufferSize       = _fftSize * _downsampleFactor;
              _temporalWindow   = _bufferSize / _samplerate;
              _maxHarmFrequency = _fftSize / _harmonics * _frequencyResolution;

              /* this will throw an exception if the fft size is not valid */
              fft     = new FFT(_fftSize, _effectiveSamplerate);
              samples = new Ring(_bufferSize, 512);

              _windowFunction.length = _bufferSize;
            }
        }
      , "effectiveSamplerate" : {
            enumerable   : true
          , configurable : false
          , get : function () {
            return _effectiveSamplerate;
            }
        }
      , "frequencyResolution" : {
            enumerable   : true
          , configurable : false
          , get : function () {
              return _frequencyResolution;
            }
        }
      , "temporalWindow" : {
            enumerable   : true
          , configurable : false
          , get : function () {
              return _temporalWindow;
            }
        }
      , "frequencyMap" : {
            enumerable   : false
          , configurable : false
          , get : function () {
              return _frequencyMap;
            }
          , set : function (freqMap) {

              if (freqMap.hasOwnProperty("closestNote")) {
                _frequencyMap   = freqMap;
              } else {
                throw new TypeError("Passed object has to have a " +
                                    "a method named closestNote");
              }
            }
        }
      , "harmonics" : {
            enumerable   : true
          , configurable : false
          , get : function () {
              return _harmonics;
            }
          , set : function (value) {

              checkNat("downsampleFactor",value);

              _harmonics        = value;
              _maxHarmFrequency = _fftSize / _harmonics * _frequencyResolution;

            }
        }
      , "maxDetectableFundamental" : {
            enumerable   : true
          , configurable : false
          , get : function () {
              return _maxHarmFrequency;
          }
      }
    });

    Object.defineProperties(Tuner.prototype, {
        "fundamental" : {
            value : function () {

              var downsampled = []
                , spectrum
                , peek
                , i
                ;

              _windowFunction.process(samples);

              for (i = 0; i < samples.length ; i += _downsampleFactor) {
                downsampled.push(samples.get(i));
              }

              fft.forward(downsampled);

              spectrum = fft.spectrum;
              peek     = HPS(spectrum,_harmonics);

              _viewCallback({
                  peek : _frequencyMap.closestNote(peek * _frequencyResolution),
                  spectrum : spectrum
              });
            }
          , enumerable   : false
          , configurable : false
          , writable     : false
        }
      , "run" : {
            value : function () {

              processor.onaudioprocess = function (event) {
                var input = event.inputBuffer.getChannelData(0);

                samples.concat(input);

                event.outputBuffer.getChannelData(0).set(input);
              };

              source.connect(lowpass);
              lowpass.connect(highpass);
              highpass.connect(processor);
              processor.connect(context.destination);

              return window.setInterval(this.fundamental,
                                      _temporalWindow.toFixed(3) * 1000);

            }
          , enumerable   : false
          , configurable : false
          , writable     : false
        }
    });

  }

  return Tuner;
}());
<commit_msg>Do not set audio stream at instantiationw.<commit_after>var Tuner = (function () {

  function Tuner(callback) {

    if (Tuner.prototype._instance) {
      return Tuner.prototype._instance;
    }

    if (!callback) {
      throw new PropertyNotInitialized("Tuner", "callback");
    }

    Tuner.prototype._instance = this;

    var context              = new AudioContext()
      , _samplerate          = 44100
      , _downsampleFactor    = 12
      , _fftSize             = 2048
      , _effectiveSamplerate = _samplerate / _downsampleFactor
      , _frequencyResolution = _effectiveSamplerate / _fftSize
      , _bufferSize          = _fftSize * _downsampleFactor
      , _temporalWindow      = _bufferSize / _samplerate
      , _harmonics           = 5
      , _maxHarmFrequency    = _fftSize / _harmonics * _frequencyResolution
      , fft                  = new FFT(_fftSize, _effectiveSamplerate)
      , samples              = new Ring(_bufferSize, 512)
      , _windowFunction      = new WindowObject("Hann", _bufferSize)
      , _frequencyMap        = new FrequencyMap()
      , _viewCallback        = callback
      ;

    var source
      , lowpass
      , highpass
      , processor
      ;

    Object.defineProperties(this, {
        "setAudioStream": {
            enumebrable  : false
          , configurable : false
          , set: function (stream) {
              source    = context.createMediaStreamSource(stream);
              lowpass   = context.createBiquadFilter();
              highpass  = context.createBiquadFilter();
              processor = context.createScriptProcessor(512,1,1);

              lowpass.type       = "lowpass";
              highpass.type      = "highpass";
              lowpass.frequency  = (_effectiveSamplerate / 2).toFixed(3);
              highpass.frequency = 35;
          }
        },
        "samplerate" : {
            value        : _samplerate
          , enumerable   : true
          , configurable : false
          , writable     : false
        }
      , "viewCallback" : {
            value        : _viewCallback
          , enumerable   : false
          , configurable : false
          , writable     : true
        }
      , "downsampleFactor" : {
            enumerable   : true
          , configurable : false
          , get : function () {
              return _downsampleFactor;
            }
          , set : function (value) {

              checkNat("downsampleFactor",value);

              _downsampleFactor = Math.floor(value);

              _effectiveSamplerate = _samplerate / _downsampleFactor;
              _frequencyResolution = _effectiveSamplerate / _fftSize;
              _bufferSize       = _fftSize * _downsampleFactor;
              _temporalWindow   = _bufferSize / _samplerate;
              _maxHarmFrequency = _fftSize / _harmonics * _frequencyResolution;

              fft     = new FFT(_fftSize, _effectiveSamplerate);
              samples = new Ring(_bufferSize, 512);

              lowpass.frequency  = (_effectiveSamplerate / 2).toFixed(3);

              _windowFunction.length = _bufferSize;
            }
        }
      , "fftSize" : {
            enumerable   : true
          , configurable : false
          , get : function () {
              return _fftSize;
            }
          , set : function (value) {
              _fftSize = value;

              _frequencyResolution = _effectiveSamplerate / _fftSize;
              _bufferSize       = _fftSize * _downsampleFactor;
              _temporalWindow   = _bufferSize / _samplerate;
              _maxHarmFrequency = _fftSize / _harmonics * _frequencyResolution;

              /* this will throw an exception if the fft size is not valid */
              fft     = new FFT(_fftSize, _effectiveSamplerate);
              samples = new Ring(_bufferSize, 512);

              _windowFunction.length = _bufferSize;
            }
        }
      , "effectiveSamplerate" : {
            enumerable   : true
          , configurable : false
          , get : function () {
            return _effectiveSamplerate;
            }
        }
      , "frequencyResolution" : {
            enumerable   : true
          , configurable : false
          , get : function () {
              return _frequencyResolution;
            }
        }
      , "temporalWindow" : {
            enumerable   : true
          , configurable : false
          , get : function () {
              return _temporalWindow;
            }
        }
      , "frequencyMap" : {
            enumerable   : false
          , configurable : false
          , get : function () {
              return _frequencyMap;
            }
          , set : function (freqMap) {

              if (freqMap.hasOwnProperty("closestNote")) {
                _frequencyMap   = freqMap;
              } else {
                throw new TypeError("Passed object has to have a " +
                                    "a method named closestNote");
              }
            }
        }
      , "harmonics" : {
            enumerable   : true
          , configurable : false
          , get : function () {
              return _harmonics;
            }
          , set : function (value) {

              checkNat("downsampleFactor",value);

              _harmonics        = value;
              _maxHarmFrequency = _fftSize / _harmonics * _frequencyResolution;

            }
        }
      , "maxDetectableFundamental" : {
            enumerable   : true
          , configurable : false
          , get : function () {
              return _maxHarmFrequency;
          }
      }
    });

    Object.defineProperties(Tuner.prototype, {
        "fundamental" : {
            value : function () {

              var downsampled = []
                , spectrum
                , peek
                , i
                ;

              _windowFunction.process(samples);

              for (i = 0; i < samples.length ; i += _downsampleFactor) {
                downsampled.push(samples.get(i));
              }

              fft.forward(downsampled);

              spectrum = fft.spectrum;
              peek     = HPS(spectrum,_harmonics);

              _viewCallback({
                  peek : _frequencyMap.closestNote(peek * _frequencyResolution),
                  spectrum : spectrum
              });
            }
          , enumerable   : false
          , configurable : false
          , writable     : false
        }
      , "run" : {
            value : function () {

              if (!source) {
                throw new ReferenceError("The audio stream is not set.");
              }

              processor.onaudioprocess = function (event) {
                var input = event.inputBuffer.getChannelData(0);

                samples.concat(input);

                event.outputBuffer.getChannelData(0).set(input);
              };

              source.connect(lowpass);
              lowpass.connect(highpass);
              highpass.connect(processor);
              processor.connect(context.destination);

              return window.setInterval(this.fundamental,
                                      _temporalWindow.toFixed(3) * 1000);

            }
          , enumerable   : false
          , configurable : false
          , writable     : false
        }
    });

  }

  return Tuner;
}());
<|endoftext|>


<commit_before>//Carto focus + Context with autonome functions

function getMockData(dataIndex) {
  var d3data;
  //Appelle main.php de manière synchrone. C'est à dire, attend la réponse avant de continuer
  $.ajax({
    type: 'GET',
    url: '/data/d3data',
    dataType: 'json',
    success: function(t_data) {
      d3data = t_data[dataIndex];
      return false;
    },
    error: function(t_data) {
      console.log("Erreur Ajax : Message=" + t_data + " (Fonction getD3Data()) !");
    },
    async: false
  });
  return d3data;
}

function menuInitialisation(myGraph) {

  if (myGraph.config.debug) console.log("checkboxInitialisation start");

  $('#focusContextNodeOff').hide();

  if (myGraph.config.curvesLinks == 'On')
    $('#curvesLinksCheckbox').checkbox('check');
  else
    $('#curvesLinksCheckbox').checkbox('uncheck');

  if (myGraph.config.openNodeOnHover == 'On')
    $('#openNodeOnHoverCheckbox').checkbox('check');
  else
    $('#openNodeOnHoverCheckbox').checkbox('uncheck');

  if (myGraph.config.force == 'On')
    $('#activeForceCheckbox').checkbox('check');
  else
    $('#activeForceCheckbox').checkbox('uncheck');

  if (myGraph.config.elastic == 'On')
    $('#activeElasticCheckbox').checkbox('check');
  else
    $('#activeElasticCheckbox').checkbox('uncheck');

  if (myGraph.config.displayId == 'On')
    $('#displayIdCheckbox').checkbox('check');
  else
    $('#displayIdCheckbox').checkbox('uncheck');

  if (myGraph.typeLdpServer == 'external')
  {
    $('#typeLdpServerCheckbox').checkbox('check');
    $('#graphNameSegment').hide();
  }
  else
  {
    $('#typeLdpServerCheckbox').checkbox('uncheck');
    $('#graphNameSegment').show();
  }

  if (myGraph.config.makeLinkSelectingNode == 'On')
    $('#LinkCreationModeCheckbox').checkbox('check');
  else
    $('#LinkCreationModeCheckbox').checkbox('uncheck');

  if (myGraph.config.debug) console.log("checkboxInitialisation end");
}

$(document).ready()
{
  var myGraph = new FluidGraph();

  myGraph.initSvgContainer("#chart");
  myGraph.mockData0 = getMockData(0);

  var store = new MyStore({ container : myGraph.externalStore.uri,
                            context : myGraph.externalStore.context,
                            template : "",
                            partials : ""})

  // location.hash = #https://ldp.virtual-assembly.org:8443/2013/cartopair/2a1499b5dc
  if (window.location.hash)
  {
    // myGraph.ldpGraphName = https://ldp.virtual-assembly.org:8443/2013/cartopair/2a1499b5dc
    myGraph.ldpGraphName = window.location.hash.substring(1);
    myGraph.openedGraph = myGraph.ldpGraphName;
    // myGraph.graphName = 2a1499b5dc
    myGraph.graphName = myGraph.openedGraph.split("/").pop();
    // myGraph.externalStore.uri = https://ldp.virtual-assembly.org:8443/2013/cartopair/
    myGraph.externalStore.uri = myGraph.openedGraph.split(myGraph.graphName)[0];
    myGraph.typeLdpServer = "external";
  }
  else
    myGraph.openedGraph = myGraph.getOpenedGraph();

  var testGraph;
  if (myGraph.openedGraph)
  {
    testGraph = myGraph.loadGraph(myGraph.typeLdpServer, myGraph.openedGraph);
  }
  else {
    //Load default graph (Démo, explaination...)
    myGraph.d3Data = getMockData(3);
  }

  var checkboxIsInitialized = false;
  menuInitialisation(myGraph);

  if (myGraph.config.force == "On") {
    myGraph.activateForce();
  } else {
    $('#activeElasticCheckbox').addClass('disabled');
  }

  checkboxIsInitialized = true;

  if (testGraph == true)
  {
    if (myGraph.d3Data.nodes.length > 0)
    {
      myGraph.drawGraph();
      if (thisGraph.config.force == "Off")
      {
        thisGraph.movexy.call(thisGraph);
      }
    }
  }
}
<commit_msg>News on fluidgraph, and test to visualise foaf JSONLD graph<commit_after>//Carto focus + Context with autonome functions

function getMockData(dataIndex) {
  var d3data;
  //Appelle main.php de manière synchrone. C'est à dire, attend la réponse avant de continuer
  $.ajax({
    type: 'GET',
    url: '/data/d3data',
    dataType: 'json',
    success: function(t_data) {
      d3data = t_data[dataIndex];
      return false;
    },
    error: function(t_data) {
      console.log("Erreur Ajax : Message=" + t_data + " (Fonction getD3Data()) !");
    },
    async: false
  });
  return d3data;
}

function menuInitialisation(myGraph) {

  if (myGraph.config.debug) console.log("checkboxInitialisation start");

  $('#focusContextNodeOff').hide();

  if (myGraph.config.curvesLinks == 'On')
    $('#curvesLinksCheckbox').checkbox('check');
  else
    $('#curvesLinksCheckbox').checkbox('uncheck');

  if (myGraph.config.openNodeOnHover == 'On')
    $('#openNodeOnHoverCheckbox').checkbox('check');
  else
    $('#openNodeOnHoverCheckbox').checkbox('uncheck');

  if (myGraph.config.force == 'On')
    $('#activeForceCheckbox').checkbox('check');
  else
    $('#activeForceCheckbox').checkbox('uncheck');

  if (myGraph.config.elastic == 'On')
    $('#activeElasticCheckbox').checkbox('check');
  else
    $('#activeElasticCheckbox').checkbox('uncheck');

  if (myGraph.config.displayId == 'On')
    $('#displayIdCheckbox').checkbox('check');
  else
    $('#displayIdCheckbox').checkbox('uncheck');

  if (myGraph.typeLdpServer == 'external')
  {
    $('#typeLdpServerCheckbox').checkbox('check');
    $('#graphNameSegment').hide();
  }
  else
  {
    $('#typeLdpServerCheckbox').checkbox('uncheck');
    $('#graphNameSegment').show();
  }

  if (myGraph.config.makeLinkSelectingNode == 'On')
    $('#LinkCreationModeCheckbox').checkbox('check');
  else
    $('#LinkCreationModeCheckbox').checkbox('uncheck');

  if (myGraph.config.debug) console.log("checkboxInitialisation end");
}

$(document).ready()
{
  var myGraph = new FluidGraph();

  myGraph.initSvgContainer("#chart");
  myGraph.mockData0 = getMockData(0);

  var store = new MyStore({ container : myGraph.externalStore.uri,
                            context : myGraph.externalStore.context,
                            template : "",
                            partials : ""})

  // location.hash = #https://ldp.virtual-assembly.org:8443/2013/cartopair/2a1499b5dc
  if (window.location.hash)
  {
    // myGraph.ldpGraphName = https://ldp.virtual-assembly.org:8443/2013/cartopair/2a1499b5dc
    myGraph.ldpGraphName = window.location.hash.substring(1);
    myGraph.openedGraph = myGraph.ldpGraphName;
    // myGraph.graphName = 2a1499b5dc
    myGraph.graphName = myGraph.openedGraph.split("/").pop();
    // myGraph.externalStore.uri = https://ldp.virtual-assembly.org:8443/2013/cartopair/
    myGraph.externalStore.uri = myGraph.openedGraph.split(myGraph.graphName)[0];
    myGraph.typeLdpServer = "external";
  }
  else
    myGraph.openedGraph = myGraph.getOpenedGraph();

  myGraph.openedGraph = "http://benoit-alessandroni.fr/ldp/actor/benoit-alessandroni/";
  myGraph.typeLdpServer = "external";

  var testGraph;
  if (myGraph.openedGraph)
  {
    testGraph = myGraph.loadGraph(myGraph.typeLdpServer, myGraph.openedGraph);
  }
  else {
    //Load default graph (Démo, explaination...)
    myGraph.d3Data = getMockData(3);
  }

  var checkboxIsInitialized = false;
  menuInitialisation(myGraph);

  if (myGraph.config.force == "On") {
    myGraph.activateForce();
  } else {
    $('#activeElasticCheckbox').addClass('disabled');
  }

  checkboxIsInitialized = true;

  if (testGraph == true)
  {
    if (myGraph.d3Data.nodes.length > 0)
    {
      myGraph.drawGraph();
      if (thisGraph.config.force == "Off")
      {
        thisGraph.movexy.call(thisGraph);
      }
    }
  }
}
<|endoftext|>


<commit_before>import Vuetify from './util/vuetify'
import Directives from './directives/_index'
import Components from './components/_index'

function plugin(Vue) {
  if (Vue.prototype.hasOwnProperty('$vuetify')) {
    return
  }

  Object.keys(Directives).forEach(key => {
    Vue.directive(key, Directives[key])
  })

  Object.keys(Components).forEach(key => {
    Vue.component(key, Components[key])
  })

  Vue.vuetify = Vuetify

  Object.defineProperty(Vue.prototype, '$vuetify', {
    get: () => Vue.vuetify
  })
}

module.exports = plugin<commit_msg>moved vuetify js into base index<commit_after>import Bus from './util/bus'
import Components from './components/_index'
import Directives from './directives/_index'
import Load from './util/load'
import Toast from './functions/toast'

function plugin(Vue) {
  if (plugin.installed) {
    return
  }

  Object.keys(Directives).forEach(key => {
    Vue.directive(key, Directives[key])
  })

  Object.keys(Components).forEach(key => {
    Vue.component(key, Components[key])
  })

  Vue.prototype.$vuetify = {
    bus: Bus,

    load: Load,

    init () {
      document.body.addEventListener('click', e => {
        Bus.pub('body:click', e)
      })
    },

    toast: Toast
  }
}

module.exports = plugin<|endoftext|>


<commit_before>"use strict";

function generate(data) {
    let equation, n, sumX, sumY, sumXY, sumPowX, aveY, aveX, pointsArray;

    pointsArray = [];

    equation = {
        a: 0,
        b: 0
    };

    n = sumX = sumY = sumXY = sumPowX = aveY = aveX = 0;
    let i;

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

        if (data[i].y !== null) {
            n++;
            sumX += data[i].x;
            sumY += data[i].y;
            sumXY += data[i].x * data[i].y;
            sumPowX += Math.pow(data[i].x,2);
        }
    }
    aveY = sumY / n;
    aveX = sumX / n;
    equation.b = (n * sumXY - sumX * sumY) / (n * sumPowX - sumX * sumX);
    equation.a = aveY - equation.b * aveX;

    for (i = 0; i < data.length; i++) {
        pointsArray.push(
            {
                x: data[i].x,
                y: trend(equation, data[i].y)
            }
        );
    }

    return {
        equation: equation,
        points: data,
        pattern: "y = " + equation.a + " + " + equation.b + " * x"
    };
}

function trend(equation, point) {
    return equation.a + equation.b * point;
}

module.exports = { generate, trend };<commit_msg>Adds pointsArray as points<commit_after>"use strict";

function generate(data) {
    let equation, n, sumX, sumY, sumXY, sumPowX, aveY, aveX, pointsArray;

    pointsArray = [];

    equation = {
        a: 0,
        b: 0
    };

    n = sumX = sumY = sumXY = sumPowX = aveY = aveX = 0;
    let i;

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

        if (data[i].y !== null) {
            n++;
            sumX += data[i].x;
            sumY += data[i].y;
            sumXY += data[i].x * data[i].y;
            sumPowX += Math.pow(data[i].x,2);
        }
    }
    aveY = sumY / n;
    aveX = sumX / n;
    equation.b = (n * sumXY - sumX * sumY) / (n * sumPowX - sumX * sumX);
    equation.a = aveY - equation.b * aveX;

    for (i = 0; i < data.length; i++) {
        pointsArray.push(
            {
                x: data[i].x,
                y: trend(equation, data[i].y)
            }
        );
    }

    return {
        equation: equation,
        points: pointsArray,
        pattern: "y = " + equation.a + " + " + equation.b + " * x"
    };
}

function trend(equation, point) {
    return equation.a + equation.b * point;
}

module.exports = { generate, trend };<|endoftext|>


<commit_before>// @flow strict

import find from '../polyfills/find';
import objectValues from '../polyfills/objectValues';

import inspect from '../jsutils/inspect';
import toObjMap from '../jsutils/toObjMap';
import devAssert from '../jsutils/devAssert';
import instanceOf from '../jsutils/instanceOf';
import isObjectLike from '../jsutils/isObjectLike';
import defineToStringTag from '../jsutils/defineToStringTag';
import {
  type ObjMap,
  type ReadOnlyObjMap,
  type ReadOnlyObjMapLike,
} from '../jsutils/ObjMap';

import { type GraphQLError } from '../error/GraphQLError';
import {
  type SchemaDefinitionNode,
  type SchemaExtensionNode,
} from '../language/ast';

import { __Schema } from './introspection';
import {
  GraphQLDirective,
  isDirective,
  specifiedDirectives,
} from './directives';
import {
  type GraphQLType,
  type GraphQLNamedType,
  type GraphQLAbstractType,
  type GraphQLObjectType,
  type GraphQLInterfaceType,
  isObjectType,
  isInterfaceType,
  isUnionType,
  isInputObjectType,
  getNamedType,
} from './definition';

/**
 * Test if the given value is a GraphQL schema.
 */
declare function isSchema(schema: mixed): boolean %checks(schema instanceof
  GraphQLSchema);
// eslint-disable-next-line no-redeclare
export function isSchema(schema) {
  return instanceOf(schema, GraphQLSchema);
}

export function assertSchema(schema: mixed): GraphQLSchema {
  if (!isSchema(schema)) {
    throw new Error(`Expected ${inspect(schema)} to be a GraphQL schema.`);
  }
  return schema;
}

/**
 * Schema Definition
 *
 * A Schema is created by supplying the root types of each type of operation,
 * query and mutation (optional). A schema definition is then supplied to the
 * validator and executor.
 *
 * Example:
 *
 *     const MyAppSchema = new GraphQLSchema({
 *       query: MyAppQueryRootType,
 *       mutation: MyAppMutationRootType,
 *     })
 *
 * Note: When the schema is constructed, by default only the types that are
 * reachable by traversing the root types are included, other types must be
 * explicitly referenced.
 *
 * Example:
 *
 *     const characterInterface = new GraphQLInterfaceType({
 *       name: 'Character',
 *       ...
 *     });
 *
 *     const humanType = new GraphQLObjectType({
 *       name: 'Human',
 *       interfaces: [characterInterface],
 *       ...
 *     });
 *
 *     const droidType = new GraphQLObjectType({
 *       name: 'Droid',
 *       interfaces: [characterInterface],
 *       ...
 *     });
 *
 *     const schema = new GraphQLSchema({
 *       query: new GraphQLObjectType({
 *         name: 'Query',
 *         fields: {
 *           hero: { type: characterInterface, ... },
 *         }
 *       }),
 *       ...
 *       // Since this schema references only the `Character` interface it's
 *       // necessary to explicitly list the types that implement it if
 *       // you want them to be included in the final schema.
 *       types: [humanType, droidType],
 *     })
 *
 * Note: If an array of `directives` are provided to GraphQLSchema, that will be
 * the exact list of directives represented and allowed. If `directives` is not
 * provided then a default set of the specified directives (e.g. @include and
 * @skip) will be used. If you wish to provide *additional* directives to these
 * specified directives, you must explicitly declare them. Example:
 *
 *     const MyAppSchema = new GraphQLSchema({
 *       ...
 *       directives: specifiedDirectives.concat([ myCustomDirective ]),
 *     })
 *
 */
export class GraphQLSchema {
  extensions: ?ReadOnlyObjMap<mixed>;
  astNode: ?SchemaDefinitionNode;
  extensionASTNodes: ?$ReadOnlyArray<SchemaExtensionNode>;

  _queryType: ?GraphQLObjectType;
  _mutationType: ?GraphQLObjectType;
  _subscriptionType: ?GraphQLObjectType;
  _directives: $ReadOnlyArray<GraphQLDirective>;
  _typeMap: TypeMap;
  _implementations: ObjMap<InterfaceImplementations>;
  _subTypeMap: ObjMap<ObjMap<boolean>>;
  // Used as a cache for validateSchema().
  __validationErrors: ?$ReadOnlyArray<GraphQLError>;

  constructor(config: GraphQLSchemaConfig): void {
    // If this schema was built from a source known to be valid, then it may be
    // marked with assumeValid to avoid an additional type system validation.
    if (config && config.assumeValid) {
      this.__validationErrors = [];
    } else {
      this.__validationErrors = undefined;

      // Otherwise check for common mistakes during construction to produce
      // clear and early error messages.
      devAssert(isObjectLike(config), 'Must provide configuration object.');
      devAssert(
        !config.types || Array.isArray(config.types),
        `"types" must be Array if provided but got: ${inspect(config.types)}.`,
      );
      devAssert(
        !config.directives || Array.isArray(config.directives),
        '"directives" must be Array if provided but got: ' +
          `${inspect(config.directives)}.`,
      );
    }

    this.extensions = config.extensions && toObjMap(config.extensions);
    this.astNode = config.astNode;
    this.extensionASTNodes = config.extensionASTNodes;

    this._queryType = config.query;
    this._mutationType = config.mutation;
    this._subscriptionType = config.subscription;
    // Provide specified directives (e.g. @include and @skip) by default.
    this._directives = config.directives || specifiedDirectives;

    // Build type map now to detect any errors within this schema.
    const initialTypes: Array<?GraphQLNamedType> = [
      this._queryType,
      this._mutationType,
      this._subscriptionType,
      __Schema,
    ].concat(config.types);

    // Keep track of all types referenced within the schema.
    let typeMap: TypeMap = Object.create(null);

    // First by deeply visiting all initial types.
    typeMap = initialTypes.reduce(typeMapReducer, typeMap);

    // Then by deeply visiting all directive types.
    typeMap = this._directives.reduce(typeMapDirectiveReducer, typeMap);

    // Storing the resulting map for reference by the schema.
    this._typeMap = typeMap;

    this._subTypeMap = Object.create(null);

    // Keep track of all implementations by interface name.
    this._implementations = collectImplementations(objectValues(typeMap));
  }

  getQueryType(): ?GraphQLObjectType {
    return this._queryType;
  }

  getMutationType(): ?GraphQLObjectType {
    return this._mutationType;
  }

  getSubscriptionType(): ?GraphQLObjectType {
    return this._subscriptionType;
  }

  getTypeMap(): TypeMap {
    return this._typeMap;
  }

  getType(name: string): ?GraphQLNamedType {
    return this.getTypeMap()[name];
  }

  getPossibleTypes(
    abstractType: GraphQLAbstractType,
  ): $ReadOnlyArray<GraphQLObjectType> {
    return isUnionType(abstractType)
      ? abstractType.getTypes()
      : this.getImplementations(abstractType).objects;
  }

  getImplementations(
    interfaceType: GraphQLInterfaceType,
  ): InterfaceImplementations {
    return this._implementations[interfaceType.name];
  }

  // @deprecated: use isSubType instead - will be removed in v16.
  isPossibleType(
    abstractType: GraphQLAbstractType,
    possibleType: GraphQLObjectType,
  ): boolean {
    return this.isSubType(abstractType, possibleType);
  }

  isSubType(
    abstractType: GraphQLAbstractType,
    maybeSubType: GraphQLObjectType | GraphQLInterfaceType,
  ): boolean {
    let map = this._subTypeMap[abstractType.name];
    if (map === undefined) {
      map = Object.create(null);

      if (isUnionType(abstractType)) {
        for (const type of abstractType.getTypes()) {
          map[type.name] = true;
        }
      } else {
        const implementations = this.getImplementations(abstractType);
        for (const type of implementations.objects) {
          map[type.name] = true;
        }
        for (const type of implementations.interfaces) {
          map[type.name] = true;
        }
      }

      this._subTypeMap[abstractType.name] = map;
    }
    return map[maybeSubType.name] !== undefined;
  }

  getDirectives(): $ReadOnlyArray<GraphQLDirective> {
    return this._directives;
  }

  getDirective(name: string): ?GraphQLDirective {
    return find(this.getDirectives(), directive => directive.name === name);
  }

  toConfig(): {|
    ...GraphQLSchemaConfig,
    types: Array<GraphQLNamedType>,
    directives: Array<GraphQLDirective>,
    extensions: ?ReadOnlyObjMap<mixed>,
    extensionASTNodes: ?$ReadOnlyArray<SchemaExtensionNode>,
    assumeValid: boolean,
  |} {
    return {
      query: this.getQueryType(),
      mutation: this.getMutationType(),
      subscription: this.getSubscriptionType(),
      types: objectValues(this.getTypeMap()),
      directives: this.getDirectives().slice(),
      extensions: this.extensions,
      astNode: this.astNode,
      extensionASTNodes: this.extensionASTNodes,
      assumeValid: this.__validationErrors !== undefined,
    };
  }
}

// Conditionally apply `[Symbol.toStringTag]` if `Symbol`s are supported
defineToStringTag(GraphQLSchema);

type TypeMap = ObjMap<GraphQLNamedType>;

type InterfaceImplementations = {|
  objects: $ReadOnlyArray<GraphQLObjectType>,
  interfaces: $ReadOnlyArray<GraphQLInterfaceType>,
|};

export type GraphQLSchemaValidationOptions = {|
  /**
   * When building a schema from a GraphQL service's introspection result, it
   * might be safe to assume the schema is valid. Set to true to assume the
   * produced schema is valid.
   *
   * Default: false
   */
  assumeValid?: boolean,
|};

export type GraphQLSchemaConfig = {|
  query?: ?GraphQLObjectType,
  mutation?: ?GraphQLObjectType,
  subscription?: ?GraphQLObjectType,
  types?: ?Array<GraphQLNamedType>,
  directives?: ?Array<GraphQLDirective>,
  extensions?: ?ReadOnlyObjMapLike<mixed>,
  astNode?: ?SchemaDefinitionNode,
  extensionASTNodes?: ?$ReadOnlyArray<SchemaExtensionNode>,
  ...GraphQLSchemaValidationOptions,
|};

function collectImplementations(
  types: $ReadOnlyArray<GraphQLNamedType>,
): ObjMap<InterfaceImplementations> {
  const implementationsMap = Object.create(null);

  for (const type of types) {
    if (isInterfaceType(type)) {
      if (implementationsMap[type.name] === undefined) {
        implementationsMap[type.name] = { objects: [], interfaces: [] };
      }

      // Store implementations by interface.
      for (const iface of type.getInterfaces()) {
        if (isInterfaceType(iface)) {
          const implementations = implementationsMap[iface.name];
          if (implementations === undefined) {
            implementationsMap[iface.name] = {
              objects: [],
              interfaces: [type],
            };
          } else {
            implementations.interfaces.push(type);
          }
        }
      }
    } else if (isObjectType(type)) {
      // Store implementations by objects.
      for (const iface of type.getInterfaces()) {
        if (isInterfaceType(iface)) {
          const implementations = implementationsMap[iface.name];
          if (implementations === undefined) {
            implementationsMap[iface.name] = {
              objects: [type],
              interfaces: [],
            };
          } else {
            implementations.objects.push(type);
          }
        }
      }
    }
  }

  return implementationsMap;
}

function typeMapReducer(map: TypeMap, type: ?GraphQLType): TypeMap {
  if (!type) {
    return map;
  }

  const namedType = getNamedType(type);
  const seenType = map[namedType.name];
  if (seenType) {
    if (seenType !== namedType) {
      throw new Error(
        `Schema must contain uniquely named types but contains multiple types named "${namedType.name}".`,
      );
    }
    return map;
  }
  map[namedType.name] = namedType;

  let reducedMap = map;

  if (isUnionType(namedType)) {
    reducedMap = namedType.getTypes().reduce(typeMapReducer, reducedMap);
  }

  if (isObjectType(namedType) || isInterfaceType(namedType)) {
    reducedMap = namedType.getInterfaces().reduce(typeMapReducer, reducedMap);

    for (const field of objectValues(namedType.getFields())) {
      const fieldArgTypes = field.args.map(arg => arg.type);
      reducedMap = fieldArgTypes.reduce(typeMapReducer, reducedMap);
      reducedMap = typeMapReducer(reducedMap, field.type);
    }
  }

  if (isInputObjectType(namedType)) {
    for (const field of objectValues(namedType.getFields())) {
      reducedMap = typeMapReducer(reducedMap, field.type);
    }
  }

  return reducedMap;
}

function typeMapDirectiveReducer(
  map: TypeMap,
  directive: ?GraphQLDirective,
): TypeMap {
  // Directives are not validated until validateSchema() is called.
  if (!isDirective(directive)) {
    return map;
  }
  return directive.args.reduce(
    (_map, arg) => typeMapReducer(_map, arg.type),
    map,
  );
}
<commit_msg>Small optimization in the schema type map reducer (#2305)<commit_after>// @flow strict

import find from '../polyfills/find';
import objectValues from '../polyfills/objectValues';

import inspect from '../jsutils/inspect';
import toObjMap from '../jsutils/toObjMap';
import devAssert from '../jsutils/devAssert';
import instanceOf from '../jsutils/instanceOf';
import isObjectLike from '../jsutils/isObjectLike';
import defineToStringTag from '../jsutils/defineToStringTag';
import {
  type ObjMap,
  type ReadOnlyObjMap,
  type ReadOnlyObjMapLike,
} from '../jsutils/ObjMap';

import { type GraphQLError } from '../error/GraphQLError';
import {
  type SchemaDefinitionNode,
  type SchemaExtensionNode,
} from '../language/ast';

import { __Schema } from './introspection';
import {
  GraphQLDirective,
  isDirective,
  specifiedDirectives,
} from './directives';
import {
  type GraphQLType,
  type GraphQLNamedType,
  type GraphQLAbstractType,
  type GraphQLObjectType,
  type GraphQLInterfaceType,
  isObjectType,
  isInterfaceType,
  isUnionType,
  isInputObjectType,
  getNamedType,
} from './definition';

/**
 * Test if the given value is a GraphQL schema.
 */
declare function isSchema(schema: mixed): boolean %checks(schema instanceof
  GraphQLSchema);
// eslint-disable-next-line no-redeclare
export function isSchema(schema) {
  return instanceOf(schema, GraphQLSchema);
}

export function assertSchema(schema: mixed): GraphQLSchema {
  if (!isSchema(schema)) {
    throw new Error(`Expected ${inspect(schema)} to be a GraphQL schema.`);
  }
  return schema;
}

/**
 * Schema Definition
 *
 * A Schema is created by supplying the root types of each type of operation,
 * query and mutation (optional). A schema definition is then supplied to the
 * validator and executor.
 *
 * Example:
 *
 *     const MyAppSchema = new GraphQLSchema({
 *       query: MyAppQueryRootType,
 *       mutation: MyAppMutationRootType,
 *     })
 *
 * Note: When the schema is constructed, by default only the types that are
 * reachable by traversing the root types are included, other types must be
 * explicitly referenced.
 *
 * Example:
 *
 *     const characterInterface = new GraphQLInterfaceType({
 *       name: 'Character',
 *       ...
 *     });
 *
 *     const humanType = new GraphQLObjectType({
 *       name: 'Human',
 *       interfaces: [characterInterface],
 *       ...
 *     });
 *
 *     const droidType = new GraphQLObjectType({
 *       name: 'Droid',
 *       interfaces: [characterInterface],
 *       ...
 *     });
 *
 *     const schema = new GraphQLSchema({
 *       query: new GraphQLObjectType({
 *         name: 'Query',
 *         fields: {
 *           hero: { type: characterInterface, ... },
 *         }
 *       }),
 *       ...
 *       // Since this schema references only the `Character` interface it's
 *       // necessary to explicitly list the types that implement it if
 *       // you want them to be included in the final schema.
 *       types: [humanType, droidType],
 *     })
 *
 * Note: If an array of `directives` are provided to GraphQLSchema, that will be
 * the exact list of directives represented and allowed. If `directives` is not
 * provided then a default set of the specified directives (e.g. @include and
 * @skip) will be used. If you wish to provide *additional* directives to these
 * specified directives, you must explicitly declare them. Example:
 *
 *     const MyAppSchema = new GraphQLSchema({
 *       ...
 *       directives: specifiedDirectives.concat([ myCustomDirective ]),
 *     })
 *
 */
export class GraphQLSchema {
  extensions: ?ReadOnlyObjMap<mixed>;
  astNode: ?SchemaDefinitionNode;
  extensionASTNodes: ?$ReadOnlyArray<SchemaExtensionNode>;

  _queryType: ?GraphQLObjectType;
  _mutationType: ?GraphQLObjectType;
  _subscriptionType: ?GraphQLObjectType;
  _directives: $ReadOnlyArray<GraphQLDirective>;
  _typeMap: TypeMap;
  _implementations: ObjMap<InterfaceImplementations>;
  _subTypeMap: ObjMap<ObjMap<boolean>>;
  // Used as a cache for validateSchema().
  __validationErrors: ?$ReadOnlyArray<GraphQLError>;

  constructor(config: GraphQLSchemaConfig): void {
    // If this schema was built from a source known to be valid, then it may be
    // marked with assumeValid to avoid an additional type system validation.
    if (config && config.assumeValid) {
      this.__validationErrors = [];
    } else {
      this.__validationErrors = undefined;

      // Otherwise check for common mistakes during construction to produce
      // clear and early error messages.
      devAssert(isObjectLike(config), 'Must provide configuration object.');
      devAssert(
        !config.types || Array.isArray(config.types),
        `"types" must be Array if provided but got: ${inspect(config.types)}.`,
      );
      devAssert(
        !config.directives || Array.isArray(config.directives),
        '"directives" must be Array if provided but got: ' +
          `${inspect(config.directives)}.`,
      );
    }

    this.extensions = config.extensions && toObjMap(config.extensions);
    this.astNode = config.astNode;
    this.extensionASTNodes = config.extensionASTNodes;

    this._queryType = config.query;
    this._mutationType = config.mutation;
    this._subscriptionType = config.subscription;
    // Provide specified directives (e.g. @include and @skip) by default.
    this._directives = config.directives || specifiedDirectives;

    // Build type map now to detect any errors within this schema.
    const initialTypes: Array<?GraphQLNamedType> = [
      this._queryType,
      this._mutationType,
      this._subscriptionType,
      __Schema,
    ].concat(config.types);

    // Keep track of all types referenced within the schema.
    let typeMap: TypeMap = Object.create(null);

    // First by deeply visiting all initial types.
    typeMap = initialTypes.reduce(typeMapReducer, typeMap);

    // Then by deeply visiting all directive types.
    typeMap = this._directives.reduce(typeMapDirectiveReducer, typeMap);

    // Storing the resulting map for reference by the schema.
    this._typeMap = typeMap;

    this._subTypeMap = Object.create(null);

    // Keep track of all implementations by interface name.
    this._implementations = collectImplementations(objectValues(typeMap));
  }

  getQueryType(): ?GraphQLObjectType {
    return this._queryType;
  }

  getMutationType(): ?GraphQLObjectType {
    return this._mutationType;
  }

  getSubscriptionType(): ?GraphQLObjectType {
    return this._subscriptionType;
  }

  getTypeMap(): TypeMap {
    return this._typeMap;
  }

  getType(name: string): ?GraphQLNamedType {
    return this.getTypeMap()[name];
  }

  getPossibleTypes(
    abstractType: GraphQLAbstractType,
  ): $ReadOnlyArray<GraphQLObjectType> {
    return isUnionType(abstractType)
      ? abstractType.getTypes()
      : this.getImplementations(abstractType).objects;
  }

  getImplementations(
    interfaceType: GraphQLInterfaceType,
  ): InterfaceImplementations {
    return this._implementations[interfaceType.name];
  }

  // @deprecated: use isSubType instead - will be removed in v16.
  isPossibleType(
    abstractType: GraphQLAbstractType,
    possibleType: GraphQLObjectType,
  ): boolean {
    return this.isSubType(abstractType, possibleType);
  }

  isSubType(
    abstractType: GraphQLAbstractType,
    maybeSubType: GraphQLObjectType | GraphQLInterfaceType,
  ): boolean {
    let map = this._subTypeMap[abstractType.name];
    if (map === undefined) {
      map = Object.create(null);

      if (isUnionType(abstractType)) {
        for (const type of abstractType.getTypes()) {
          map[type.name] = true;
        }
      } else {
        const implementations = this.getImplementations(abstractType);
        for (const type of implementations.objects) {
          map[type.name] = true;
        }
        for (const type of implementations.interfaces) {
          map[type.name] = true;
        }
      }

      this._subTypeMap[abstractType.name] = map;
    }
    return map[maybeSubType.name] !== undefined;
  }

  getDirectives(): $ReadOnlyArray<GraphQLDirective> {
    return this._directives;
  }

  getDirective(name: string): ?GraphQLDirective {
    return find(this.getDirectives(), directive => directive.name === name);
  }

  toConfig(): {|
    ...GraphQLSchemaConfig,
    types: Array<GraphQLNamedType>,
    directives: Array<GraphQLDirective>,
    extensions: ?ReadOnlyObjMap<mixed>,
    extensionASTNodes: ?$ReadOnlyArray<SchemaExtensionNode>,
    assumeValid: boolean,
  |} {
    return {
      query: this.getQueryType(),
      mutation: this.getMutationType(),
      subscription: this.getSubscriptionType(),
      types: objectValues(this.getTypeMap()),
      directives: this.getDirectives().slice(),
      extensions: this.extensions,
      astNode: this.astNode,
      extensionASTNodes: this.extensionASTNodes,
      assumeValid: this.__validationErrors !== undefined,
    };
  }
}

// Conditionally apply `[Symbol.toStringTag]` if `Symbol`s are supported
defineToStringTag(GraphQLSchema);

type TypeMap = ObjMap<GraphQLNamedType>;

type InterfaceImplementations = {|
  objects: $ReadOnlyArray<GraphQLObjectType>,
  interfaces: $ReadOnlyArray<GraphQLInterfaceType>,
|};

export type GraphQLSchemaValidationOptions = {|
  /**
   * When building a schema from a GraphQL service's introspection result, it
   * might be safe to assume the schema is valid. Set to true to assume the
   * produced schema is valid.
   *
   * Default: false
   */
  assumeValid?: boolean,
|};

export type GraphQLSchemaConfig = {|
  query?: ?GraphQLObjectType,
  mutation?: ?GraphQLObjectType,
  subscription?: ?GraphQLObjectType,
  types?: ?Array<GraphQLNamedType>,
  directives?: ?Array<GraphQLDirective>,
  extensions?: ?ReadOnlyObjMapLike<mixed>,
  astNode?: ?SchemaDefinitionNode,
  extensionASTNodes?: ?$ReadOnlyArray<SchemaExtensionNode>,
  ...GraphQLSchemaValidationOptions,
|};

function collectImplementations(
  types: $ReadOnlyArray<GraphQLNamedType>,
): ObjMap<InterfaceImplementations> {
  const implementationsMap = Object.create(null);

  for (const type of types) {
    if (isInterfaceType(type)) {
      if (implementationsMap[type.name] === undefined) {
        implementationsMap[type.name] = { objects: [], interfaces: [] };
      }

      // Store implementations by interface.
      for (const iface of type.getInterfaces()) {
        if (isInterfaceType(iface)) {
          const implementations = implementationsMap[iface.name];
          if (implementations === undefined) {
            implementationsMap[iface.name] = {
              objects: [],
              interfaces: [type],
            };
          } else {
            implementations.interfaces.push(type);
          }
        }
      }
    } else if (isObjectType(type)) {
      // Store implementations by objects.
      for (const iface of type.getInterfaces()) {
        if (isInterfaceType(iface)) {
          const implementations = implementationsMap[iface.name];
          if (implementations === undefined) {
            implementationsMap[iface.name] = {
              objects: [type],
              interfaces: [],
            };
          } else {
            implementations.objects.push(type);
          }
        }
      }
    }
  }

  return implementationsMap;
}

function typeMapReducer(map: TypeMap, type: ?GraphQLType): TypeMap {
  if (!type) {
    return map;
  }

  const namedType = getNamedType(type);
  const seenType = map[namedType.name];
  if (seenType) {
    if (seenType !== namedType) {
      throw new Error(
        `Schema must contain uniquely named types but contains multiple types named "${namedType.name}".`,
      );
    }
    return map;
  }
  map[namedType.name] = namedType;

  let reducedMap = map;

  if (isUnionType(namedType)) {
    reducedMap = namedType.getTypes().reduce(typeMapReducer, reducedMap);
  } else if (isObjectType(namedType) || isInterfaceType(namedType)) {
    reducedMap = namedType.getInterfaces().reduce(typeMapReducer, reducedMap);

    for (const field of objectValues(namedType.getFields())) {
      const fieldArgTypes = field.args.map(arg => arg.type);
      reducedMap = fieldArgTypes.reduce(typeMapReducer, reducedMap);
      reducedMap = typeMapReducer(reducedMap, field.type);
    }
  } else if (isInputObjectType(namedType)) {
    for (const field of objectValues(namedType.getFields())) {
      reducedMap = typeMapReducer(reducedMap, field.type);
    }
  }

  return reducedMap;
}

function typeMapDirectiveReducer(
  map: TypeMap,
  directive: ?GraphQLDirective,
): TypeMap {
  // Directives are not validated until validateSchema() is called.
  if (!isDirective(directive)) {
    return map;
  }
  return directive.args.reduce(
    (_map, arg) => typeMapReducer(_map, arg.type),
    map,
  );
}
<|endoftext|>


<commit_before>import Promise from 'bluebird';
import getYouTubeID from 'get-youtube-id';
import parseIsoDuration from 'parse-iso-duration';
import getArtistTitle, { fallBackToArtist } from 'get-artist-title';
import chunk from 'chunk';
import values from 'object-values';

import googleapis from 'googleapis';

const youTube = googleapis.youtube('v3');

const youTubeSearch = Promise.promisify(youTube.search.list);
const youTubeGet = Promise.promisify(youTube.videos.list);
const youTubeGetChannels = Promise.promisify(youTube.channels.list);
const youTubeGetPlaylists = Promise.promisify(youTube.playlists.list);
const youTubeGetPlaylistItems = Promise.promisify(youTube.playlistItems.list);

function parseYouTubeDuration(duration) {
  return Math.round(parseIsoDuration(duration) / 1000);
}

function getBestThumbnail(thumbnails) {
  if (thumbnails) {
    if (thumbnails.high) {
      return thumbnails.high.url;
    } else if (thumbnails.medium) {
      return thumbnails.medium.url;
    } else if (thumbnails.default) {
      return thumbnails.default.url;
    }
  }
  return '';
}

function getBlockedCountryCodes(contentDetails) {
  if (contentDetails.regionRestriction) {
    return contentDetails.regionRestriction.blocked || [];
  }
  return [];
}

function normalizeMedia(video) {
  const [artist, title] = getArtistTitle(video.snippet.title, [
    'base', fallBackToArtist(video.snippet.channelTitle)
  ]);

  return {
    sourceID: video.id,
    // TODO Fix the detection in get-artist-title so that it doesn't split the
    // title into parts with only fluff.
    artist: artist || '[unknown]',
    title: title || '[unknown]',
    duration: parseYouTubeDuration(video.contentDetails.duration),
    thumbnail: getBestThumbnail(video.snippet.thumbnails),
    sourceData: {
      blockedIn: getBlockedCountryCodes(video.contentDetails)
    }
  };
}

const rxChannelUrl = /youtube\.com\/channel\/([^/?#]+)/i;
const rxUserUrl = /youtube\.com\/user\/([^/?#]+)/i;

const rxSimplePlaylistUrl = /youtube\.com\/playlist\?.*?list=([a-z0-9_-]+)/i;
const rxPlaylistID = /^([a-z0-9_-]+)$/i;

/**
 * Extract a playlist ID from a playlist URL.
 */
export function getPlaylistID(url) {
  if (rxPlaylistID.test(url)) {
    return url;
  }

  const match = url.match(rxSimplePlaylistUrl);
  if (match) {
    return match[1];
  }

  return null;
}

const defaultSearchOptions = {
  part: 'id',
  fields: `
    items(id/videoId),
    pageInfo,
    nextPageToken,
    prevPageToken
  `.replace(/\s+/g, ''),
  type: 'video',
  maxResults: 50,
  safeSearch: 'none',
  videoSyndicated: true
};

const getPlaylistsOptions = {
  part: 'snippet,contentDetails',
  fields: `
    items(
      id,
      snippet/title,
      snippet/description,
      snippet/channelTitle,
      snippet/thumbnails,
      contentDetails/itemCount
    ),
    pageInfo,
    nextPageToken
  `.replace(/\s+/g, ''),
  maxResults: 50
};

export default function youTubeSource(uw, opts = {}) {
  if (!opts.key) {
    throw new TypeError(
      'Expected a YouTube API key in "options.key". For information on how to ' +
      'configure your YouTube API access, see ' +
      'https://developers.google.com/youtube/v3/getting-started.'
    );
  }

  const params = opts.key ? { key: opts.key } : {};
  const searchOptions = opts.search || {};

  async function getPage(sourceIDs) {
    const result = await youTubeGet({
      ...params,
      part: 'snippet,contentDetails',
      fields: `
        items(
          id,
          snippet(title, channelTitle, thumbnails),
          contentDetails(duration, regionRestriction)
        )
      `.replace(/\s+/g, ''),
      id: sourceIDs.join(',')
    });

    return result.items.map(normalizeMedia);
  }

  async function get(sourceIDs) {
    const pages = await Promise.all(
      chunk(sourceIDs, 50).map(getPage)
    );
    return pages.reduce((result, page) => result.concat(page), []);
  }

  async function search(query, page = null) {
    const result = await youTubeSearch({
      ...defaultSearchOptions,
      ...searchOptions,
      ...params,
      // When searching for a video URL, we want to search for the video ID
      // only, because search results are very inconsistent with some types of
      // URLs.
      q: getYouTubeID(query, { fuzzy: false }) || query,
      pageToken: page
    });

    return get(result.items.map(item => item.id.videoId));
  }

  async function getPlaylistPage(playlistID, page = null) {
    const result = await youTubeGetPlaylistItems({
      ...params,
      part: 'contentDetails',
      playlistId: playlistID,
      maxResults: 50,
      pageToken: page
    });

    return {
      nextPage: result.nextPageToken,
      items: result.items
    };
  }

  async function getPlaylistItems(playlistID) {
    let page = null;
    const playlistItems = [];
    do {
      const res = await getPlaylistPage(playlistID, page);
      page = res.nextPage;
      playlistItems.push(...res.items);
    } while (page);

    const ids = playlistItems.map(item => item.contentDetails.videoId);
    const medias = await get(ids);

    return medias.map(media => ({
      ...media,
      start: 0,
      end: media.duration
    }));
  }

  async function getPlaylistMeta(playlistID) {
    const result = await youTubeGetPlaylists({
      ...params,
      part: 'snippet',
      fields: 'items(id,snippet/title)',
      id: playlistID,
      maxResults: 1
    });
    return result.items[0];
  }

  async function getImportablePlaylist(url) {
    const playlistID = getPlaylistID(url);
    if (!playlistID) {
      throw new Error(
        'Invalid playlist URL. Please provide a direct link to the playlist ' +
        'you want to import.'
      );
    }
    const playlist = await getPlaylistMeta(playlistID);
    const items = await getPlaylistItems(playlistID);
    return {
      playlist: {
        sourceID: playlist.id,
        name: playlist.snippet.title
      },
      items
    };
  }

  async function getChannelMeta(url) {
    let match = url.match(rxChannelUrl);
    const request = {
      ...params,
      part: 'snippet,contentDetails',
      fields: `
        items(
          id,
          snippet/title,
          contentDetails/relatedPlaylists/favorites,
          contentDetails/relatedPlaylists/uploads
        )
      `.replace(/\s+/g, ''),
      maxResults: 1
    };
    if (match) {
      request.id = match[1];
    } else {
      match = url.match(rxUserUrl);
      if (match) {
        request.forUsername = match[1];
      } else {
        throw new Error(
          'Invalid channel URL. Please provide a direct link to the channel or ' +
          'user you want to import playlists from.'
        );
      }
    }

    const result = await youTubeGetChannels(request);

    const channel = result.items[0];
    return {
      id: channel.id,
      title: channel.snippet.title,
      playlists: channel.contentDetails.relatedPlaylists
    };
  }

  async function getChannelPlaylistsPage(channelID, page = null) {
    const result = await youTubeGetPlaylists({
      ...params,
      ...getPlaylistsOptions,
      channelId: channelID,
      pageToken: page
    });

    return {
      nextPage: result.nextPageToken,
      items: result.items
    };
  }

  async function getChannelPlaylists(channelID) {
    const playlists = [];
    let page;
    do {
      const res = await getChannelPlaylistsPage(channelID, page);
      page = res.nextPage;
      playlists.push(...res.items);
    } while (page);

    return playlists;
  }

  async function getPlaylistMetasForUser(url) {
    const channel = await getChannelMeta(url);

    const specials = youTubeGetPlaylists({
      ...params,
      ...getPlaylistsOptions,
      id: values(channel.playlists)
    });
    const playlists = getChannelPlaylists(channel.id);

    const result = await Promise.all([specials, playlists]);

    const allPlaylists = [
      ...result[0][0].items,
      ...result[1]
    ];

    return {
      channel: { id: channel.id, title: channel.title },
      playlists: allPlaylists.map(item => ({
        sourceID: item.id,
        sourceChannel: item.snippet.channelTitle,
        name: item.snippet.title,
        description: item.snippet.description,
        size: item.contentDetails.itemCount,
        thumbnail: getBestThumbnail(item.snippet.thumbnails)
      }))
    };
  }

  async function doImport(ctx, name, playlistID) {
    const items = await getPlaylistItems(playlistID);
    return await ctx.createPlaylist(name, items);
  }

  return {
    search,
    get: get, // eslint-disable-line object-shorthand
    async 'import'(ctx, action) {
      if (action.action === 'channel') {
        return await getPlaylistMetasForUser(action.url);
      }
      if (action.action === 'playlist') {
        const importable = await getImportablePlaylist(action.url);
        importable.items = ctx.source.addSourceType(importable.items);
        return importable;
      }
      if (action.action === 'importplaylist') {
        return await doImport(ctx, action.name, action.id);
      }

      throw new TypeError(`Unknown action "${action}"`);
    }
  };
}
<commit_msg>improve playlist item import error message, fixes #2<commit_after>import Promise from 'bluebird';
import getYouTubeID from 'get-youtube-id';
import parseIsoDuration from 'parse-iso-duration';
import getArtistTitle, { fallBackToArtist } from 'get-artist-title';
import chunk from 'chunk';
import values from 'object-values';

import googleapis from 'googleapis';

const youTube = googleapis.youtube('v3');

const youTubeSearch = Promise.promisify(youTube.search.list);
const youTubeGet = Promise.promisify(youTube.videos.list);
const youTubeGetChannels = Promise.promisify(youTube.channels.list);
const youTubeGetPlaylists = Promise.promisify(youTube.playlists.list);
const youTubeGetPlaylistItems = Promise.promisify(youTube.playlistItems.list);

function parseYouTubeDuration(duration) {
  return Math.round(parseIsoDuration(duration) / 1000);
}

function getBestThumbnail(thumbnails) {
  if (thumbnails) {
    if (thumbnails.high) {
      return thumbnails.high.url;
    } else if (thumbnails.medium) {
      return thumbnails.medium.url;
    } else if (thumbnails.default) {
      return thumbnails.default.url;
    }
  }
  return '';
}

function getBlockedCountryCodes(contentDetails) {
  if (contentDetails.regionRestriction) {
    return contentDetails.regionRestriction.blocked || [];
  }
  return [];
}

function normalizeMedia(video) {
  const [artist, title] = getArtistTitle(video.snippet.title, [
    'base', fallBackToArtist(video.snippet.channelTitle)
  ]);

  return {
    sourceID: video.id,
    // TODO Fix the detection in get-artist-title so that it doesn't split the
    // title into parts with only fluff.
    artist: artist || '[unknown]',
    title: title || '[unknown]',
    duration: parseYouTubeDuration(video.contentDetails.duration),
    thumbnail: getBestThumbnail(video.snippet.thumbnails),
    sourceData: {
      blockedIn: getBlockedCountryCodes(video.contentDetails)
    }
  };
}

const rxChannelUrl = /youtube\.com\/channel\/([^/?#]+)/i;
const rxUserUrl = /youtube\.com\/user\/([^/?#]+)/i;

const rxSimplePlaylistUrl = /youtube\.com\/playlist\?.*?list=([a-z0-9_-]+)/i;
const rxPlaylistID = /^([a-z0-9_-]+)$/i;

/**
 * Extract a playlist ID from a playlist URL.
 */
export function getPlaylistID(url) {
  if (rxPlaylistID.test(url)) {
    return url;
  }

  const match = url.match(rxSimplePlaylistUrl);
  if (match) {
    return match[1];
  }

  return null;
}

const defaultSearchOptions = {
  part: 'id',
  fields: `
    items(id/videoId),
    pageInfo,
    nextPageToken,
    prevPageToken
  `.replace(/\s+/g, ''),
  type: 'video',
  maxResults: 50,
  safeSearch: 'none',
  videoSyndicated: true
};

const getPlaylistsOptions = {
  part: 'snippet,contentDetails',
  fields: `
    items(
      id,
      snippet/title,
      snippet/description,
      snippet/channelTitle,
      snippet/thumbnails,
      contentDetails/itemCount
    ),
    pageInfo,
    nextPageToken
  `.replace(/\s+/g, ''),
  maxResults: 50
};

export default function youTubeSource(uw, opts = {}) {
  if (!opts.key) {
    throw new TypeError(
      'Expected a YouTube API key in "options.key". For information on how to ' +
      'configure your YouTube API access, see ' +
      'https://developers.google.com/youtube/v3/getting-started.'
    );
  }

  const params = opts.key ? { key: opts.key } : {};
  const searchOptions = opts.search || {};

  async function getPage(sourceIDs) {
    const result = await youTubeGet({
      ...params,
      part: 'snippet,contentDetails',
      fields: `
        items(
          id,
          snippet(title, channelTitle, thumbnails),
          contentDetails(duration, regionRestriction)
        )
      `.replace(/\s+/g, ''),
      id: sourceIDs.join(',')
    });

    return result.items.map(normalizeMedia);
  }

  async function get(sourceIDs) {
    const pages = await Promise.all(
      chunk(sourceIDs, 50).map(getPage)
    );
    return pages.reduce((result, page) => result.concat(page), []);
  }

  async function search(query, page = null) {
    const result = await youTubeSearch({
      ...defaultSearchOptions,
      ...searchOptions,
      ...params,
      // When searching for a video URL, we want to search for the video ID
      // only, because search results are very inconsistent with some types of
      // URLs.
      q: getYouTubeID(query, { fuzzy: false }) || query,
      pageToken: page
    });

    return get(result.items.map(item => item.id.videoId));
  }

  async function getPlaylistPage(playlistID, page = null) {
    const result = await youTubeGetPlaylistItems({
      ...params,
      part: 'contentDetails',
      playlistId: playlistID,
      maxResults: 50,
      pageToken: page
    });

    return {
      nextPage: result.nextPageToken,
      items: result.items
    };
  }

  async function getPlaylistItems(playlistID) {
    let page = null;
    const playlistItems = [];
    try {
      do {
        const res = await getPlaylistPage(playlistID, page);
        page = res.nextPage;
        playlistItems.push(...res.items);
      } while (page);
    } catch (e) {
      throw new Error(
        'That playlist could not be imported. If it\'s a private playlist, ' +
        'change its visibility to Unlisted and try again.'
      );
    }

    const ids = playlistItems.map(item => item.contentDetails.videoId);
    const medias = await get(ids);

    return medias.map(media => ({
      ...media,
      start: 0,
      end: media.duration
    }));
  }

  async function getPlaylistMeta(playlistID) {
    const result = await youTubeGetPlaylists({
      ...params,
      part: 'snippet',
      fields: 'items(id,snippet/title)',
      id: playlistID,
      maxResults: 1
    });
    return result.items[0];
  }

  async function getImportablePlaylist(url) {
    const playlistID = getPlaylistID(url);
    if (!playlistID) {
      throw new Error(
        'Invalid playlist URL. Please provide a direct link to the playlist ' +
        'you want to import.'
      );
    }
    const playlist = await getPlaylistMeta(playlistID);
    const items = await getPlaylistItems(playlistID);
    return {
      playlist: {
        sourceID: playlist.id,
        name: playlist.snippet.title
      },
      items
    };
  }

  async function getChannelMeta(url) {
    let match = url.match(rxChannelUrl);
    const request = {
      ...params,
      part: 'snippet,contentDetails',
      fields: `
        items(
          id,
          snippet/title,
          contentDetails/relatedPlaylists/favorites,
          contentDetails/relatedPlaylists/uploads
        )
      `.replace(/\s+/g, ''),
      maxResults: 1
    };
    if (match) {
      request.id = match[1];
    } else {
      match = url.match(rxUserUrl);
      if (match) {
        request.forUsername = match[1];
      } else {
        throw new Error(
          'Invalid channel URL. Please provide a direct link to the channel or ' +
          'user you want to import playlists from.'
        );
      }
    }

    const result = await youTubeGetChannels(request);

    const channel = result.items[0];
    return {
      id: channel.id,
      title: channel.snippet.title,
      playlists: channel.contentDetails.relatedPlaylists
    };
  }

  async function getChannelPlaylistsPage(channelID, page = null) {
    const result = await youTubeGetPlaylists({
      ...params,
      ...getPlaylistsOptions,
      channelId: channelID,
      pageToken: page
    });

    return {
      nextPage: result.nextPageToken,
      items: result.items
    };
  }

  async function getChannelPlaylists(channelID) {
    const playlists = [];
    let page;
    do {
      const res = await getChannelPlaylistsPage(channelID, page);
      page = res.nextPage;
      playlists.push(...res.items);
    } while (page);

    return playlists;
  }

  async function getPlaylistMetasForUser(url) {
    const channel = await getChannelMeta(url);

    const specials = youTubeGetPlaylists({
      ...params,
      ...getPlaylistsOptions,
      id: values(channel.playlists)
    });
    const playlists = getChannelPlaylists(channel.id);

    const result = await Promise.all([specials, playlists]);

    const allPlaylists = [
      ...result[0][0].items,
      ...result[1]
    ];

    return {
      channel: { id: channel.id, title: channel.title },
      playlists: allPlaylists.map(item => ({
        sourceID: item.id,
        sourceChannel: item.snippet.channelTitle,
        name: item.snippet.title,
        description: item.snippet.description,
        size: item.contentDetails.itemCount,
        thumbnail: getBestThumbnail(item.snippet.thumbnails)
      }))
    };
  }

  async function doImport(ctx, name, playlistID) {
    const items = await getPlaylistItems(playlistID);
    return await ctx.createPlaylist(name, items);
  }

  return {
    search,
    get: get, // eslint-disable-line object-shorthand
    async 'import'(ctx, action) {
      if (action.action === 'channel') {
        return await getPlaylistMetasForUser(action.url);
      }
      if (action.action === 'playlist') {
        const importable = await getImportablePlaylist(action.url);
        importable.items = ctx.source.addSourceType(importable.items);
        return importable;
      }
      if (action.action === 'importplaylist') {
        return await doImport(ctx, action.name, action.id);
      }

      throw new TypeError(`Unknown action "${action}"`);
    }
  };
}
<|endoftext|>


<commit_before>Util.Animation = u.a = new function() {

	// translate3d support?
	this.support3d = function() {
		// only run detection once
		if(this._support3d === undefined) {
			var node = document.createElement("div");
			try {
				var test = "translate3d(10px, 10px, 10px)";
				node.style[this.vendor("Transform")] = test;

//				u.bug("3d test:" + test + "::" + node.style[this.vendor("Transform")]);
				if(node.style[this.vendor("Transform")] == test) {
					this._support3d = true;
				}
				else {
					this._support3d = false;
				}
			}
			catch(exception) {
//				u.bug("exception:" + exception)
				this._support3d = false;
			}
		}
//		u.bug("3d test result:" + this._support3d);
		return this._support3d;
	}

//	alert("rest")

	// get vendor, to avoid setting more than the required type
	// exceptions to be aware of
	this._vendor_exceptions = {
		"mozTransform":"MozTransform","mozTransition":"MozTransition","mozTransitionEnd":"transitionend","msTransitionEnd":"transitionend","mozTransformOrigin":"MozTransformOrigin","mozPerspectiveOrigin":"MozPerspectiveOrigin","mozTransformStyle":"MozTransformStyle","mozPerspective":"MozPerspective","mozBackfaceVisibility":"MozBackfaceVisibility"
	};
	// method cache - when a vendor method has been requested once,
	// it will be stored, to avoid wasting time every time
	this._vendor_methods = {};

	// find correct method
	// checks for exceptions and stores method in cache (_vendor_methods)
 	this.vendorMethod = function(method) {
		var vender_method = this._vendor+method;
		method = this._vendor ? method.replace(/^([a-z]{1})/, function(word){return word.toUpperCase()}) : method;

		if(this._vendor_exceptions[this._vendor+method]) {
			this._vendor_methods[vender_method] = this._vendor_exceptions[this._vendor+method];
			return;
		}
 		this._vendor_methods[vender_method] = this._vendor+method;
 		return;
	}

	// get vendor and optional method name
	// returns name from cache (_vendor_methods)
	// or checks method using vendorMethod
	this.vendor = function(method) {
//		u.bug("vendor: "+ this._vendor + ":" + method);

		// only run detection once
		if(this._vendor === undefined) {
//			u.bug("no implementation")

			if(document.documentElement.style.webkitTransform != undefined) {
//			if(document.body.style.webkitTransform != undefined) {
//				u.bug("vendor: webkit")
				this._vendor = "webkit";
			}
			else if(document.documentElement.style.MozTransform != undefined) {
//			else if(document.body.style.MozTransform != undefined) {
//				u.bug("vendor: moz")
				this._vendor = "moz";
			}
			else if(document.documentElement.style.oTransform != undefined) {
//			else if(document.body.style.oTransform != undefined) {
//				u.bug("vendor: o")
				this._vendor = "o";
			}
			else if(document.documentElement.style.msTransform != undefined) {
//			else if(document.body.style.msTransform != undefined) {
//				u.bug("vendor: ms")
				this._vendor = "ms";
			}
			else {
//				u.bug("vendor: unknown")
				this._vendor = "";
			}
		}

		if(!method) {
			return this._vendor;
		}

		if(this._vendor_methods[this._vendor+method] === undefined) {
			this.vendorMethod(method);
		}

//		u.bug(this._vendor_methods[this._vendor+method] + ", method:" + method)
		return this._vendor_methods[this._vendor+method];
	}

	/**
	* Apply CSS transition to node
	*/
	this.transition = function(node, transition, callback) {
//		u.bug("add transition:" + u.nodeId(node) + ", " + transition + ", " + callback);

		try {

			// get duration
			var duration = transition.match(/[0-9.]+[ms]+/g);
			if(duration) {
		//		u.bug(duration[0]);
				node.duration = duration[0].match("ms") ? parseFloat(duration[0]) : (parseFloat(duration[0]) * 1000);

				// custom transition callback
				if(callback) {
//					u.bug("custom transition callback:" + callback + ", " + u.nodeId(node))

					var transitioned;
					transitioned = (function(event) {

//						u.bug("custom transitioned:" + callback  + ", " + u.nodeId(event.target) + ", " + u.nodeId(this) + ", " + typeof(callback) + ", " + typeof(this[callback]))
						u.e.removeEvent(event.target, u.a.vendor("transitionEnd"), transitioned);

						if(event.target == this) {

							u.a.transition(this, "none");

							if(typeof(callback) == "function") {
								var key = u.randomString(4);
								node[key] = callback;
								node[key].callback(event);
								node[key] = null;
								callback = null;
							}
							else if(typeof(this[callback]) == "function") {
//								u.bug("callback to: " + callback + ", " + this[callback])
								this[callback](event);
								this[callback] = null;
							}

						}
						else {
//							u.e.kill(event);
						}
					});

					u.e.addEvent(node, this.vendor("transitionEnd"), transitioned);
				}
				else {
//					u.bug("standard transition callback:" + u.nodeId(node))

					// only set transitionEnd listener if transition has duration
					u.e.addEvent(node, this.vendor("transitionEnd"), this._transitioned);
				}

			}
			else {
				node.duration = false;

				// delete transitioned callback when "none" transition is set (cleanup)
				// if(transition.match(/none/i)) {
				// 	node.transitioned = null;
				// }
			}

			node.style[this.vendor("Transition")] = transition;

		}
		catch(exception) {
			u.exception("u.a.transition", arguments, exception);
		}

	}

	// transition end handler
	// not for chained transitions - will reset node.transitioned and remove transition
	this._transitioned = function(event) {

//		u.bug("default transitioned:" + u.nodeId(this))
//		u.bug("transitioned: " + u.nodeId(event.target) + ", " + u.nodeId(this) + ", " + typeof(this.transitioned))

		// remove event listener - it's job is done
		u.e.removeEvent(event.target, u.a.vendor("transitionEnd"), u.a._transitioned);

		if(event.target == this && typeof(this.transitioned) == "function") {

			this.transitioned(event);

			this.transitioned = null;

		}

		u.a.transition(event.target, "none");

	}

	// EXPERIMENTAL: remove transform, because Firefox 23 makes render-error, when returning to 0 in translates
	this.removeTransform = function(node) {
		node.style[this.vendor("Transform")] = "none";
	}

	// Set origin
	this.origin = function(node, x, y) {

		// set origin
		node.style[this.vendor("TransformOrigin")] = x +"px "+ y +"px";

		// store values
		node._origin_x = x;
		node._origin_y = y;

		// update dom
		node.offsetHeight;
	}

	/**
	* Simple translate cross-browser
	*/
	this.translate = function(node, x, y) {
		// use translate3d when supported as it is more often hardware accelerated
		if(this.support3d()) {
			node.style[this.vendor("Transform")] = "translate3d("+x+"px, "+y+"px, 0)";
		}
		else {
			node.style[this.vendor("Transform")] = "translate("+x+"px, "+y+"px)";
		}

		// new value holder
		node._x = x;
		node._y = y;

		// update dom
		node.offsetHeight;
	}

	this.rotate = function(node, deg) {
		node.style[this.vendor("Transform")] = "rotate("+deg+"deg)";
		node._rotation = deg;

		// update dom
		node.offsetHeight;
	}

	this.scale = function(node, scale) {
		node.style[this.vendor("Transform")] = "scale("+scale+")";
		node._scale = scale;

		// update dom
		node.offsetHeight;
	}

	this.setOpacity = function(node, opacity) {
		node.style.opacity = opacity;
		node._opacity = opacity;

		// update dom
		node.offsetHeight;
	}

	this.setWidth = function(node, width) {
		width = width.toString().match(/\%|auto|px/) ? width : (width + "px");
		node.style.width = width;
		node._width = width;

		// update dom
		node.offsetHeight;
	}

	this.setHeight = function(node, height) {
		height = height.toString().match(/\%|auto|px/) ? height : (height + "px");
		node.style.height = height;
		node._height = height;

		// update dom
		node.offsetHeight;
	}

	this.setBgPos = function(node, x, y) {
		x = x.toString().match(/\%|auto|px|center|top|left|bottom|right/) ? x : (x + "px");
		y = y.toString().match(/\%|auto|px|center|top|left|bottom|right/) ? y : (y + "px");
		node.style.backgroundPosition = x + " " + y;
		node._bg_x = x;
		node._bg_y = y;

		// update dom
		node.offsetHeight;
	}

	this.setBgColor = function(node, color) {
		node.style.backgroundColor = color;
		node._bg_color = color;

		// update dom
		node.offsetHeight;
	}

	// Combined Transforms. Make it possible to run several animation effects at the same time (EX: rotate + scale + translate).

	// Rotate & Scale
	this.rotateScale = function(node, deg, scale) {
		node.style[this.vendor("Transform")] = "rotate("+deg+"deg) scale("+scale+")";
		node._rotation = deg;
		node._scale = scale;

		// update dom
		node.offsetHeight;
	}

	// Scale, Rotate, Translate
	this.scaleRotateTranslate = function(node, scale, deg, x, y) {

		if(this.support3d()) {
			node.style[this.vendor("Transform")] = "scale("+scale+") rotate("+deg+"deg) translate3d("+x+"px, "+y+"px, 0)";
		}
		else {
			node.style[this.vendor("Transform")] = "scale("+scale+") rotate("+deg+"deg) translate("+x+"px, "+y+"px)";
		}

		// store value
		node._rotation = deg;
		node._scale = scale;
		node._x = x;
		node._y = y;

		// update dom
		node.offsetHeight;
	}

	// ANIMATION FRAME

	this._animationqueue = {};
	this.requestAnimationFrame = function(node, callback, duration) {
//		u.bug("requestAnimationFrame:" + callback + ", " + duration + ", " + u.nodeId(node) + ", " + u.a._requestAnimationId)

		// add animation to stack
		var start = new Date().getTime();
		var id = u.randomString();

		// create object with all information
		u.a._animationqueue[id] = {};
		u.a._animationqueue[id].id = id;
		u.a._animationqueue[id].node = node;
		u.a._animationqueue[id].callback = callback;
		u.a._animationqueue[id].start = start;
		u.a._animationqueue[id].duration = duration;

		// TODO: timers are not very precise - is this a good idea+
		// add duration timer
		u.t.setTimer(u.a, function() {u.a.finalAnimationFrame(id)}, duration);

		// first addition, set up animationframe loop
		if(!u.a._animationframe) {

			// create function references
			window._requestAnimationFrame = eval(this.vendor("requestAnimationFrame"));
			window._cancelAnimationFrame = eval(this.vendor("cancelAnimationFrame"));

			// animationframe iterator
			u.a._animationframe = function(timestamp) {

//				u.bug("frame:" + timestamp);

				var id, animation;
				for(id in u.a._animationqueue) {

					animation = u.a._animationqueue[id];

					// progress callback
					animation.node[animation.callback]((timestamp-animation.start) / animation.duration);
				}

				// continue animationFrame loop
				if(Object.keys(u.a._animationqueue).length) {

					u.a._requestAnimationId = window._requestAnimationFrame(u.a._animationframe);
				}
			}
		}

		// loop will be pause when no animations are active
		// restart requestAnimationFrame loop if it is paused
		if(!u.a._requestAnimationId) {

//			u.bug("restart")
			u.a._requestAnimationId = window._requestAnimationFrame(u.a._animationframe);
//			u.bug("u.a._requestAnimationId:" + u.a._requestAnimationId)
		}

		return id;
	}

	this.finalAnimationFrame = function(id) {
//		u.bug("finalAnimationFrame:" + ", " + id + ", " + u.a._requestAnimationId);

		var animation = u.a._animationqueue[id];
		animation.node[animation.callback](1);

		if(typeof(animation.node.transitioned) == "function") {
//			u.bug("callback:" + u.nodeId(animation.node));
			animation.node.transitioned({});
		}

		// delete animation;
		delete animation;
		delete u.a._animationqueue[id];

		// continue animationFrame loop
		if(!Object.keys(u.a._animationqueue).length) {

			this.cancelAnimationFrame(id);
		}
	}

	this.cancelAnimationFrame = function(id) {
//		u.bug("cancelAnimationFrame:" + ", " + id + ", " + u.a._requestAnimationId);

		if(id && u.a._animationqueue[id]) {

			// delete animation;
			delete u.a._animationqueue[id];
		}

		if(u.a._requestAnimationId) {

//				u.bug(this.vendor("cancelAnimationFrame"));
			window._cancelAnimationFrame(u.a._requestAnimationId);

			u.a._requestAnimationId = false;
		}
	}

}
<commit_msg>Updated CSS animation JS function name translations<commit_after>Util.Animation = u.a = new function() {

	// translate3d support?
	this.support3d = function() {
		// only run detection once
		if(this._support3d === undefined) {
			var node = document.createElement("div");
			try {
				var test = "translate3d(10px, 10px, 10px)";
				node.style[this.vendor("Transform")] = test;

//				u.bug("3d test:" + test + "::" + node.style[this.vendor("Transform")]);
				if(node.style[this.vendor("Transform")] == test) {
					this._support3d = true;
				}
				else {
					this._support3d = false;
				}
			}
			catch(exception) {
//				u.bug("exception:" + exception)
				this._support3d = false;
			}
		}
//		u.bug("3d test result:" + this._support3d);
		return this._support3d;
	}

//	alert("rest")

	// get vendor, to avoid setting more than the required type
	// exceptions to be aware of
	this._vendor_exceptions = {
		"mozTransform":"MozTransform",
		"mozTransition":"MozTransition",
		"mozTransitionEnd":"transitionend",
		"msTransitionEnd":"transitionend",
		"mozTransformOrigin":"MozTransformOrigin",
		"mozPerspectiveOrigin":"MozPerspectiveOrigin",
		"mozTransformStyle":"MozTransformStyle",
		"mozPerspective":"MozPerspective",
		"mozBackfaceVisibility":"MozBackfaceVisibility",
		"msCancelAnimationFrame":"cancelAnimationFrame"
	};
	// method cache - when a vendor method has been requested once,
	// it will be stored, to avoid wasting time every time
	this._vendor_methods = {};

	// find correct method
	// checks for exceptions and stores method in cache (_vendor_methods)
 	this.vendorMethod = function(method) {
		var vender_method = this._vendor+method;
		method = this._vendor ? method.replace(/^([a-z]{1})/, function(word){return word.toUpperCase()}) : method;

		if(this._vendor_exceptions[this._vendor+method]) {
			this._vendor_methods[vender_method] = this._vendor_exceptions[this._vendor+method];
			return;
		}
 		this._vendor_methods[vender_method] = this._vendor+method;
 		return;
	}

	// get vendor and optional method name
	// returns name from cache (_vendor_methods)
	// or checks method using vendorMethod
	this.vendor = function(method) {
//		u.bug("vendor: "+ this._vendor + ":" + method);

		// only run detection once
		if(this._vendor === undefined) {
//			u.bug("no implementation")

			if(document.documentElement.style.webkitTransform != undefined) {
//			if(document.body.style.webkitTransform != undefined) {
//				u.bug("vendor: webkit")
				this._vendor = "webkit";
			}
			else if(document.documentElement.style.MozTransform != undefined) {
//			else if(document.body.style.MozTransform != undefined) {
//				u.bug("vendor: moz")
				this._vendor = "moz";
			}
			else if(document.documentElement.style.oTransform != undefined) {
//			else if(document.body.style.oTransform != undefined) {
//				u.bug("vendor: o")
				this._vendor = "o";
			}
			else if(document.documentElement.style.msTransform != undefined) {
//			else if(document.body.style.msTransform != undefined) {
//				u.bug("vendor: ms")
				this._vendor = "ms";
			}
			else {
//				u.bug("vendor: unknown")
				this._vendor = "";
			}
		}

		if(!method) {
			return this._vendor;
		}

		if(this._vendor_methods[this._vendor+method] === undefined) {
			this.vendorMethod(method);
		}

//		u.bug(this._vendor_methods[this._vendor+method] + ", method:" + method)
		return this._vendor_methods[this._vendor+method];
	}

	/**
	* Apply CSS transition to node
	*/
	this.transition = function(node, transition, callback) {
//		u.bug("add transition:" + u.nodeId(node) + ", " + transition + ", " + callback);

		try {

			// get duration
			var duration = transition.match(/[0-9.]+[ms]+/g);
			if(duration) {
		//		u.bug(duration[0]);
				node.duration = duration[0].match("ms") ? parseFloat(duration[0]) : (parseFloat(duration[0]) * 1000);

				// custom transition callback
				if(callback) {
//					u.bug("custom transition callback:" + callback + ", " + u.nodeId(node))

					var transitioned;
					transitioned = (function(event) {

//						u.bug("custom transitioned:" + callback  + ", " + u.nodeId(event.target) + ", " + u.nodeId(this) + ", " + typeof(callback) + ", " + typeof(this[callback]))
						u.e.removeEvent(event.target, u.a.vendor("transitionEnd"), transitioned);

						if(event.target == this) {

							u.a.transition(this, "none");

							if(typeof(callback) == "function") {
								var key = u.randomString(4);
								node[key] = callback;
								node[key].callback(event);
								node[key] = null;
								callback = null;
							}
							else if(typeof(this[callback]) == "function") {
//								u.bug("callback to: " + callback + ", " + this[callback])
								this[callback](event);
								this[callback] = null;
							}

						}
						else {
//							u.e.kill(event);
						}
					});

					u.e.addEvent(node, this.vendor("transitionEnd"), transitioned);
				}
				else {
//					u.bug("standard transition callback:" + u.nodeId(node))

					// only set transitionEnd listener if transition has duration
					u.e.addEvent(node, this.vendor("transitionEnd"), this._transitioned);
				}

			}
			else {
				node.duration = false;

				// delete transitioned callback when "none" transition is set (cleanup)
				// if(transition.match(/none/i)) {
				// 	node.transitioned = null;
				// }
			}

			node.style[this.vendor("Transition")] = transition;

		}
		catch(exception) {
			u.exception("u.a.transition", arguments, exception);
		}

	}

	// transition end handler
	// not for chained transitions - will reset node.transitioned and remove transition
	this._transitioned = function(event) {

//		u.bug("default transitioned:" + u.nodeId(this))
//		u.bug("transitioned: " + u.nodeId(event.target) + ", " + u.nodeId(this) + ", " + typeof(this.transitioned))

		// remove event listener - it's job is done
		u.e.removeEvent(event.target, u.a.vendor("transitionEnd"), u.a._transitioned);

		if(event.target == this && typeof(this.transitioned) == "function") {

			this.transitioned(event);

			this.transitioned = null;

		}

		u.a.transition(event.target, "none");

	}

	// EXPERIMENTAL: remove transform, because Firefox 23 makes render-error, when returning to 0 in translates
	this.removeTransform = function(node) {
		node.style[this.vendor("Transform")] = "none";
	}

	// Set origin
	this.origin = function(node, x, y) {

		// set origin
		node.style[this.vendor("TransformOrigin")] = x +"px "+ y +"px";

		// store values
		node._origin_x = x;
		node._origin_y = y;

		// update dom
		node.offsetHeight;
	}

	/**
	* Simple translate cross-browser
	*/
	this.translate = function(node, x, y) {
		// use translate3d when supported as it is more often hardware accelerated
		if(this.support3d()) {
			node.style[this.vendor("Transform")] = "translate3d("+x+"px, "+y+"px, 0)";
		}
		else {
			node.style[this.vendor("Transform")] = "translate("+x+"px, "+y+"px)";
		}

		// new value holder
		node._x = x;
		node._y = y;

		// update dom
		node.offsetHeight;
	}

	this.rotate = function(node, deg) {
		node.style[this.vendor("Transform")] = "rotate("+deg+"deg)";
		node._rotation = deg;

		// update dom
		node.offsetHeight;
	}

	this.scale = function(node, scale) {
		node.style[this.vendor("Transform")] = "scale("+scale+")";
		node._scale = scale;

		// update dom
		node.offsetHeight;
	}

	this.setOpacity = function(node, opacity) {
		node.style.opacity = opacity;
		node._opacity = opacity;

		// update dom
		node.offsetHeight;
	}

	this.setWidth = function(node, width) {
		width = width.toString().match(/\%|auto|px/) ? width : (width + "px");
		node.style.width = width;
		node._width = width;

		// update dom
		node.offsetHeight;
	}

	this.setHeight = function(node, height) {
		height = height.toString().match(/\%|auto|px/) ? height : (height + "px");
		node.style.height = height;
		node._height = height;

		// update dom
		node.offsetHeight;
	}

	this.setBgPos = function(node, x, y) {
		x = x.toString().match(/\%|auto|px|center|top|left|bottom|right/) ? x : (x + "px");
		y = y.toString().match(/\%|auto|px|center|top|left|bottom|right/) ? y : (y + "px");
		node.style.backgroundPosition = x + " " + y;
		node._bg_x = x;
		node._bg_y = y;

		// update dom
		node.offsetHeight;
	}

	this.setBgColor = function(node, color) {
		node.style.backgroundColor = color;
		node._bg_color = color;

		// update dom
		node.offsetHeight;
	}

	// Combined Transforms. Make it possible to run several animation effects at the same time (EX: rotate + scale + translate).

	// Rotate & Scale
	this.rotateScale = function(node, deg, scale) {
		node.style[this.vendor("Transform")] = "rotate("+deg+"deg) scale("+scale+")";
		node._rotation = deg;
		node._scale = scale;

		// update dom
		node.offsetHeight;
	}

	// Scale, Rotate, Translate
	this.scaleRotateTranslate = function(node, scale, deg, x, y) {

		if(this.support3d()) {
			node.style[this.vendor("Transform")] = "scale("+scale+") rotate("+deg+"deg) translate3d("+x+"px, "+y+"px, 0)";
		}
		else {
			node.style[this.vendor("Transform")] = "scale("+scale+") rotate("+deg+"deg) translate("+x+"px, "+y+"px)";
		}

		// store value
		node._rotation = deg;
		node._scale = scale;
		node._x = x;
		node._y = y;

		// update dom
		node.offsetHeight;
	}

	// ANIMATION FRAME

	this._animationqueue = {};
	this.requestAnimationFrame = function(node, callback, duration) {
//		u.bug("requestAnimationFrame:" + callback + ", " + duration + ", " + u.nodeId(node) + ", " + u.a._requestAnimationId)

		// add animation to stack
		var start = new Date().getTime();
		var id = u.randomString();

		// create object with all information
		u.a._animationqueue[id] = {};
		u.a._animationqueue[id].id = id;
		u.a._animationqueue[id].node = node;
		u.a._animationqueue[id].callback = callback;
		u.a._animationqueue[id].start = start;
		u.a._animationqueue[id].duration = duration;

		// TODO: timers are not very precise - is this a good idea+
		// add duration timer
		u.t.setTimer(u.a, function() {u.a.finalAnimationFrame(id)}, duration);

		// first addition, set up animationframe loop
		if(!u.a._animationframe) {

			// create function references
			window._requestAnimationFrame = eval(this.vendor("requestAnimationFrame"));
			window._cancelAnimationFrame = eval(this.vendor("cancelAnimationFrame"));

			// animationframe iterator
			u.a._animationframe = function(timestamp) {

//				u.bug("frame:" + timestamp);

				var id, animation;
				for(id in u.a._animationqueue) {

					animation = u.a._animationqueue[id];

					// progress callback
					animation.node[animation.callback]((timestamp-animation.start) / animation.duration);
				}

				// continue animationFrame loop
				if(Object.keys(u.a._animationqueue).length) {

					u.a._requestAnimationId = window._requestAnimationFrame(u.a._animationframe);
				}
			}
		}

		// loop will be pause when no animations are active
		// restart requestAnimationFrame loop if it is paused
		if(!u.a._requestAnimationId) {

//			u.bug("restart")
			u.a._requestAnimationId = window._requestAnimationFrame(u.a._animationframe);
//			u.bug("u.a._requestAnimationId:" + u.a._requestAnimationId)
		}

		return id;
	}

	this.finalAnimationFrame = function(id) {
//		u.bug("finalAnimationFrame:" + ", " + id + ", " + u.a._requestAnimationId);

		var animation = u.a._animationqueue[id];
		animation.node[animation.callback](1);

		if(typeof(animation.node.transitioned) == "function") {
//			u.bug("callback:" + u.nodeId(animation.node));
			animation.node.transitioned({});
		}

		// delete animation;
		delete animation;
		delete u.a._animationqueue[id];

		// continue animationFrame loop
		if(!Object.keys(u.a._animationqueue).length) {

			this.cancelAnimationFrame(id);
		}
	}

	this.cancelAnimationFrame = function(id) {
//		u.bug("cancelAnimationFrame:" + ", " + id + ", " + u.a._requestAnimationId);

		if(id && u.a._animationqueue[id]) {

			// delete animation;
			delete u.a._animationqueue[id];
		}

		if(u.a._requestAnimationId) {

//				u.bug(this.vendor("cancelAnimationFrame"));
			window._cancelAnimationFrame(u.a._requestAnimationId);

			u.a._requestAnimationId = false;
		}
	}

}
<|endoftext|>