<commit_before>/*global Arenite:true*/
/*jshint evil:true*/
// Collection of utility functions to handle loading resources.
Arenite.Loader = function (arenite) {

  var _createCORSRequest = function (method, url) {
    var xhr = new XMLHttpRequest();
    xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
    xhr.setRequestHeader("Access-Control-Allow-Origin", window.location.origin);
    if (arenite.config.withCredentials){
    if ('withCredentials' in xhr) {
      xhr.open(method, url, true);
      xhr.withCredentials = true;
    } else if (typeof XDomainRequest !== 'undefined') {
      xhr = new XDomainRequest();
      xhr.open(method, url);
    } else {
      xhr.open(method, url, true);
    }
    return xhr;
  };

  var _loadResource = function (url, callback, error) {
    var req = _createCORSRequest('GET', url);
    req.onreadystatechange = function () {
      if (req.readyState === 4) {
        if (req.status % 100 < 4) {
          callback(req);
        } else if (typeof error === 'function') {
          error(req);
        }
      }
    };
    req.send();
  };

  var _loadScriptWithTag = function (url, callback) {
    var head = document.getElementsByTagName('head')[0];
    var script = document.createElement('script');
    script.async = true;
    script.type = 'text/javascript';
    if (navigator.appVersion.indexOf("MSIE 9") === -1) { //IE 9 calls the callback twice
      script.onreadystatechange = function () {
        if (this.readyState === 'complete') {
          callback();
        }
      };
    }
    script.onload = callback;
    script.src = url;
    head.appendChild(script);
  };

  var _loadScriptAsResource = function (url, callback) {
    _loadResource(url, function (req) {
      //analyze the script for "annotation" type configurations
      arenite.annotation.processAnnotations(req.responseText);
      var file_tag = '\n//@ sourceURL=' + window.location.origin + '/' + url + '\n//# sourceURL=' + window.location.origin + '/' + url;
      eval(req.responseText + file_tag);
      callback();
    });
  };

  var _sameOrigin = function (url) {
    var loc = window.location;
    var a = document.createElement('a');
    a.href = url;
    return a.hostname === loc.hostname &&
      a.port === loc.port &&
      a.protocol === loc.protocol;
  };

  var _loadScriptFrom = function (url, callback) {
    if (_sameOrigin(url)) {
      _loadScriptAsResource(url, callback);
    } else {
      _loadScriptWithTag(url, callback);
    }
  };

  var _loadScript = function (script, done) {
    if (typeof script === 'string') {
      _loadScriptFrom(script, done);
    } else {
      _loadScriptFrom(script.url, function () {
        arenite.object.keys(script.instances).forEach(function (instance) {
          arenite.context.add(instance, window[script.instances[instance]]);
          if (typeof script.init === 'function') {
            script.init(arenite);
          }
          delete window[script.instances[instance]];
        });
        if (typeof done === 'function') {
          done();
        }
      });
    }
  };

  return {
    loader: {
      //###loader.loadResource
      // Load a given resource using ajax.
      //

      // loadResource(url, callback, error)
      //

      //where *url* is the url to fetch from,  *callback* is a function called with the ajax request after
      // it's succesful completion and *error* is an optional callback to handle errors when fetching the resource
      loadResource: _loadResource,
      //###loader.loadScript
      // Load a script. This method will choose the best method to load the script (using tag or ajax) depending on the
      // origin of the script. Additionally it can extract variables exposed in the window object and register them as
      // instances in arenite.
      //

      // loadScript(script, callback)
      //

      //where *script* is either a url string or a structure defining a url and instances to be extracted from
      // the window object into arenite and *callback* is the callback for when the script is loaded.
      loadScript: _loadScript
    }
  };
};
<commit_msg>improving loader for IE<commit_after>/*global Arenite:true*/
/*jshint evil:true*/
// Collection of utility functions to handle loading resources.
Arenite.Loader = function (arenite) {

  var _createCORSRequest = function (method, url) {
    var xhr = new XMLHttpRequest();
    xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
    xhr.setRequestHeader("Access-Control-Allow-Origin", window.location.origin);
    if (arenite.config.withCredentials){
      if ('withCredentials' in xhr) {
        xhr.open(method, url, true);
        xhr.withCredentials = true;
      } else if (typeof XDomainRequest !== 'undefined') {
        xhr = new XDomainRequest();
        xhr.open(method, url);
      } else {
        xhr=null
      }
    } else {
      xhr.open(method, url, true);
    }
    return xhr;
  };

  var _loadResource = function (url, callback, error) {
    var req = _createCORSRequest('GET', url);
    req.onreadystatechange = function () {
      if (req.readyState === 4) {
        if (req.status % 100 < 4) {
          callback(req);
        } else if (typeof error === 'function') {
          error(req);
        }
      }
    };
    req.send();
  };

  var _loadScriptWithTag = function (url, callback) {
    var head = document.getElementsByTagName('head')[0];
    var script = document.createElement('script');
    script.async = true;
    script.type = 'text/javascript';
    if (navigator.appVersion.indexOf("MSIE 9") === -1) { //IE 9 calls the callback twice
      script.onreadystatechange = function () {
        if (this.readyState === 'complete') {
          callback();
        }
      };
    }
    script.onload = callback;
    script.src = url;
    head.appendChild(script);
  };

  var _loadScriptAsResource = function (url, callback) {
    _loadResource(url, function (req) {
      //analyze the script for "annotation" type configurations
      arenite.annotation.processAnnotations(req.responseText);
      var file_tag = '\n//@ sourceURL=' + window.location.origin + '/' + url + '\n//# sourceURL=' + window.location.origin + '/' + url;
      eval(req.responseText + file_tag);
      callback();
    });
  };

  var _sameOrigin = function (url) {
    var loc = window.location;
    var a = document.createElement('a');
    a.href = url;
    return a.hostname === loc.hostname &&
      a.port === loc.port &&
      a.protocol === loc.protocol;
  };

  var _loadScriptFrom = function (url, callback) {
    if (_sameOrigin(url)) {
      _loadScriptAsResource(url, callback);
    } else {
      _loadScriptWithTag(url, callback);
    }
  };

  var _loadScript = function (script, done) {
    if (typeof script === 'string') {
      _loadScriptFrom(script, done);
    } else {
      _loadScriptFrom(script.url, function () {
        arenite.object.keys(script.instances).forEach(function (instance) {
          arenite.context.add(instance, window[script.instances[instance]]);
          if (typeof script.init === 'function') {
            script.init(arenite);
          }
          delete window[script.instances[instance]];
        });
        if (typeof done === 'function') {
          done();
        }
      });
    }
  };

  return {
    loader: {
      //###loader.loadResource
      // Load a given resource using ajax.
      //

      // loadResource(url, callback, error)
      //

      //where *url* is the url to fetch from,  *callback* is a function called with the ajax request after
      // it's succesful completion and *error* is an optional callback to handle errors when fetching the resource
      loadResource: _loadResource,
      //###loader.loadScript
      // Load a script. This method will choose the best method to load the script (using tag or ajax) depending on the
      // origin of the script. Additionally it can extract variables exposed in the window object and register them as
      // instances in arenite.
      //

      // loadScript(script, callback)
      //

      //where *script* is either a url string or a structure defining a url and instances to be extracted from
      // the window object into arenite and *callback* is the callback for when the script is loaded.
      loadScript: _loadScript
    }
  };
};
<|endoftext|>


<commit_before>import path from "path";
import fs from "fs";
import 'babel/polyfill';
import feathers from 'feathers';
import bodyParser from 'body-parser';
import hooks from 'feathers-hooks';
import NeDB from 'feathers-nedb';
import madison from 'madison';
import cors from 'cors';

import config from './config';
import importer from './importer';
import serviceHooks from './hooks';

const apiResourcesPath = path.resolve(__dirname, "..", "node_modules", "place-my-order-assets");
if (fs.existsSync(apiResourcesPath)) {
  console.log(`URLs starting with "api/resources" will be served from "${apiResourcesPath}"`);
} else {
  console.log(`URLs starting with "api/resources" will NOT be served. The path "${apiResourcesPath}" does NOT exist.`);
}

function fromRestaurants(mapper) {
  return function(req, res, next) {
    let query = {};
    let reqQuery = req.query.filter || req.query;
    Object.keys(reqQuery).forEach(key => query[`address.${key}`] = reqQuery[key]);
    req.app.service('restaurants').find({ query },
      (error, restaurants) => {
        if(error) {
          return next(error);
        }
        res.json({ data: mapper(restaurants) });
      });
  };
}

// Allow CORS websocket connections:
const io = feathers.socketio();
io.origins('*:*');

const api = feathers()
    .configure(feathers.rest())
    .configure(io)
    .configure(hooks())
    .use(bodyParser.json())
    .use(bodyParser.urlencoded({ extended: true }))
    .use(cors())
    .get('/states', fromRestaurants(restaurants => {
      let result = {};
      restaurants.data.forEach(restaurant => {
        let short = restaurant.address.state;
        if(!result[short]) {
          result[short] = { short, name: madison.getStateNameSync(short) };
        }
      });

      return Object.keys(result).map(key => result[key]);
    }))
    .get('/cities', fromRestaurants(restaurants => {
      let result = {};
      restaurants.data.forEach(restaurant => {
        let name = restaurant.address.city;
        if(!result[name]) {
          result[name] = { name, state: restaurant.address.state };
        }
      });

      return Object.keys(result).map(key => result[key]);
    }))
    .use('/api/resources', feathers.static(apiResourcesPath))
    .use('/restaurants', new NeDB('restaurants'))
    .use('/orders', new NeDB('orders'));

  api.service('orders')
    .before(serviceHooks.addDelay(config.delay))
    .before(serviceHooks.useFilter)
    .before(serviceHooks.convertOrderItems)
    .before({
      find: serviceHooks.allowArray('status')
    }).after({
      find: serviceHooks.wrapData
    });

  api.service('restaurants')
    .before(serviceHooks.addDelay(config.delay))
    .before(serviceHooks.useFilter)
    .before({
      get: serviceHooks.alternateId('slug')
    }).after({
      find: serviceHooks.wrapData
    });

  api.service('restaurants').find({}, function(error, restaurants) {
    if(error) {
      console.error(error.message);
    }

    if(restaurants && !restaurants.data.length) {
      importer(api);
    }
  });

module.exports = api;
<commit_msg>Revert "Configure socket io to work with CORS"<commit_after>import path from "path";
import fs from "fs";
import 'babel/polyfill';
import feathers from 'feathers';
import bodyParser from 'body-parser';
import hooks from 'feathers-hooks';
import NeDB from 'feathers-nedb';
import madison from 'madison';
import cors from 'cors';

import config from './config';
import importer from './importer';
import serviceHooks from './hooks';

const apiResourcesPath = path.resolve(__dirname, "..", "node_modules", "place-my-order-assets");
if (fs.existsSync(apiResourcesPath)) {
  console.log(`URLs starting with "api/resources" will be served from "${apiResourcesPath}"`);
} else {
  console.log(`URLs starting with "api/resources" will NOT be served. The path "${apiResourcesPath}" does NOT exist.`);
}

function fromRestaurants(mapper) {
  return function(req, res, next) {
    let query = {};
    let reqQuery = req.query.filter || req.query;
    Object.keys(reqQuery).forEach(key => query[`address.${key}`] = reqQuery[key]);
    req.app.service('restaurants').find({ query },
      (error, restaurants) => {
        if(error) {
          return next(error);
        }
        res.json({ data: mapper(restaurants) });
      });
  };
}

const api = feathers()
    .configure(feathers.rest())
    .configure(feathers.socketio())
    .configure(hooks())
    .use(bodyParser.json())
    .use(bodyParser.urlencoded({ extended: true }))
    .use(cors())
    .get('/states', fromRestaurants(restaurants => {
      let result = {};
      restaurants.data.forEach(restaurant => {
        let short = restaurant.address.state;
        if(!result[short]) {
          result[short] = { short, name: madison.getStateNameSync(short) };
        }
      });

      return Object.keys(result).map(key => result[key]);
    }))
    .get('/cities', fromRestaurants(restaurants => {
      let result = {};
      restaurants.data.forEach(restaurant => {
        let name = restaurant.address.city;
        if(!result[name]) {
          result[name] = { name, state: restaurant.address.state };
        }
      });

      return Object.keys(result).map(key => result[key]);
    }))
    .use('/api/resources', feathers.static(apiResourcesPath))
    .use('/restaurants', new NeDB('restaurants'))
    .use('/orders', new NeDB('orders'));

  api.service('orders')
    .before(serviceHooks.addDelay(config.delay))
    .before(serviceHooks.useFilter)
    .before(serviceHooks.convertOrderItems)
    .before({
      find: serviceHooks.allowArray('status')
    }).after({
      find: serviceHooks.wrapData
    });

  api.service('restaurants')
    .before(serviceHooks.addDelay(config.delay))
    .before(serviceHooks.useFilter)
    .before({
      get: serviceHooks.alternateId('slug')
    }).after({
      find: serviceHooks.wrapData
    });

  api.service('restaurants').find({}, function(error, restaurants) {
    if(error) {
      console.error(error.message);
    }

    if(restaurants && !restaurants.data.length) {
      importer(api);
    }
  });

module.exports = api;
<|endoftext|>


<commit_before>var WebNES = function(nes) {
  this.nes = nes;
  window.AudioContext = window.AudioContext||window.webkitAudioContext;
  this.audio = new AudioContext();
  //this.audio = new webkitAudioContext();

  // Initialize screen context
  this.screen = document.getElementById('screen');
  this.canvasContext = this.screen.getContext('2d');
  this.canvasContext.fillStyle = 'black';
  this.canvasContext.fillRect(0, 0, 256, 240);

  // Initialize framebuffer
  this.canvasData = this.canvasContext.getImageData(0, 0, 256, 240);
  for (var i = 3; i < this.canvasData.data.length - 3; i += 4) {
      this.canvasData.data[i] = 0xFF;
  }

  // Unlock audio
  var self = this;
  $(document).bind('touchend', function() {
    var source = self.audio.createBufferSource();
    source.buffer = self.audio.createBuffer(1, 44100, 44100);
    source.connect(self.audio.destination);
    if (source.start) {
        source.start(0);
    } else {
        source.noteOn(0);
    }
  });

  var intervalId = 0;
  var startEvent = 'touchstart';
  var stopEvent = 'touchend';
  this.screen.addEventListener(startEvent, function() {
    intervalId = setInterval(function() {
      $('#home').slideDown(250);
      $('#portrait_controls').fadeOut(250);
      $('#play').slideUp(250);
      $(document).unbind('touchmove');
      nes.stop();
      clearInterval(intervalId);
    }, 1000); }, false);
  this.screen.addEventListener(stopEvent, function() {
    clearInterval(intervalId);
  }, false);
};

WebNES.prototype = {
  updateStatus: function(status) {
    //console.log('JSNES: ' + status);
  },
  writeFrame: function(buffer, prevBuffer) {
    var data = this.canvasData.data;
    for (var i = 0; i < 256 * 240; i++) {
        var pixel = buffer[i];
        if (pixel != prevBuffer[i]) {
            var j = i * 4;
            data[j] = pixel & 0xFF;
            data[j + 1] = (pixel >> 8) & 0xFF;
            data[j + 2] = (pixel >> 16) & 0xFF;
            prevBuffer[i] = pixel;
        }
    }
    this.canvasContext.putImageData(this.canvasData, 0, 0);
  },
  writeAudio: function(leftSamples, rightSamples) {
    var source = this.audio.createBufferSource();
    var buffer = this.audio.createBuffer(1, leftSamples.length, this.nes.papu.sampleRate);
    buffer.getChannelData(0).set(leftSamples);
    //buffer.getChannelData(1).set(rightSamples);
    source.buffer = buffer;
    source.connect(this.audio.destination);
    if (source.start) {
        source.start(0);
    } else {
        source.noteOn(0);
    }
  }
};

$(function() {
  h = window.screen.availHeight
  w = window.screen.availWidth

  if (h >= 640) {
    $("#portrait_up").css("top", "61%")
    $("#portrait_right").css("top", "70%")
    $("#portrait_down").css("top", "75%")
    $("#portrait_left").css("top", "70%")
    $("#portrait_select").css("top", "84%")
    $("#portrait_start").css("top", "84%")
    $("#portrait_B").css("top", "66%")
    $("#portrait_A").css("top", "60%")
  }

  if(!jQuery.browser.mobile){
    $('#home').hide();
    $('#play').hide();
    $('#desktopLanding').fadeIn(500);
  }

  var db = openDatabase('webnes', '1.0', 'Downloaded NES ROMs', 2 * 1024 * 1024);
  var nes = new JSNES({ 'ui': WebNES, fpsInterval: 2000, emulateSound: true });

  function renderItem(record) {
    var item = $('<li/>').text(record.name).attr('id', record.id);
    var alerted = false;
    var timeoutId = 0;
    var startEvent = 'touchstart';
    var stopEvent = 'touchend';
    item.bind(startEvent, function() {
      alerted = false;
      timeoutId = window.setTimeout(function() {
        alerted = true;
        if (!confirm("Delete this ROM?")) return;
        db.transaction(function(tx){
          tx.executeSql('DELETE FROM roms WHERE id = ?', [record.id], function() {
            localStorage.removeItem(record.storage);
            item.remove();
          });
        });
      }, 1000);
    }).bind(stopEvent, function() {
      clearTimeout(timeoutId);
      if (alerted) return;
      $('#home').slideUp(250);
      $('#play').slideDown(250);
      $('#portrait_controls').slideDown(250);

      if (nes.loadedId !== record.id) {
        var rom = localStorage.getItem(record.storage);
        nes.loadRom(rom);
        nes.loadedId = record.id;
      }
      $(document).bind('touchmove', function(event) {
         event.preventDefault();

		 var presses = [0,0,0,0,0,0,0,0];
  		 var testbuttons = [ 'portrait_A', 'portrait_B', 'portrait_select', 'portrait_start', 'portrait_up', 'portrait_down', 'portrait_left', 'portrait_right' ];
			//nes.input.setButton(3, true);
		 for (var i=0; i < event.originalEvent.targetTouches.length; i++) {
                var x = event.originalEvent.targetTouches[i].pageX - window.pageXOffset;
                var y = event.originalEvent.targetTouches[i].pageY - window.pageYOffset;
                var pressed = document.elementFromPoint(x, y);
				if(pressed){
					if(testbuttons.indexOf(pressed.id) >= 0 && testbuttons.indexOf(pressed.id) < presses.length){
						presses[testbuttons.indexOf(pressed.id)] = 1;
						//nes.input.setButton(testbuttons.indexOf(pressed.id), true);
						//if(!$(pressed).is(selector))
						//{
					      //nes.input.setButton(testbuttons.indexOf(target), true);
					      //nes.input.setButton(testbuttons.indexOf(selector), false);
						//}
					}
				}
   		}

		for (var i=0; i < presses.length; i++) {
			if(presses[i] == 0)
			{
				nes.input.setButton(i, false);
			}
			else
			{
				nes.input.setButton(i, true);
			}
		}
      });
      nes.start();
    });
    return item;
  };

  function addRom(name, url) {
    $.ajax({
      type: 'GET',
      url: url,
      timeout: 3000,
      mimeType: 'text/plain; charset=x-user-defined',
      success: function(data) {
        var key = Math.random().toString(36).slice(2);
        localStorage.setItem(key, data);
        db.transaction(function(tx){
          tx.executeSql('INSERT INTO roms (id, name, storage) VALUES (?, ?, ?)', [null, name, key]);
          tx.executeSql('SELECT * FROM roms WHERE storage = ?', [key], function(tx, result) {
            $('#scroll ul').append(renderItem(result.rows.item(0)));
          });
        });
      }
    });
  }

  db.transaction(function(tx) {
    tx.executeSql('CREATE TABLE IF NOT EXISTS roms (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, storage TEXT)');
    tx.executeSql('SELECT * FROM roms', [], function(tx, result) {
      for (var i = 0; i < result.rows.length; i++) {
        $('#scroll ul').append(renderItem(result.rows.item(i)));
      }
      if (result.rows.length == 0) {
        addRom('Super Mario Bros. 3', 'roms/smb3.nes');
        addRom('Pac-Man', 'roms/Pac-Man.nes');
        addRom('Galaxy Patrol', 'roms/galaxy.nes');
        addRom('Fighter F-8000', 'roms/fighter_f8000.nes');
        addRom('BoxBoy', 'roms/BOXBOY.nes');
      }
    });
  });

  $('#addROM').click(function() {
    Dropbox.choose({
      success: function(files) {
        files.forEach(function(file) {
          addRom(file.name.replace('.nes', ''), file.link);
        });
      },
      linkType: "direct",
      multiselect: true,
      extensions: ['.nes']
    });
  });

  var input = nes.input;
  var buttons = [ '#portrait_A', '#portrait_B', '#portrait_select','#portrait_start', '#portrait_up', '#portrait_down', '#portrait_left', '#portrait_right' ];
  var startEvent = 'touchstart';
  var stopEvent = 'touchend';
  var cancelEvent = 'touchcancel';
  buttons.forEach(function(selector) {
    $(selector).bind(startEvent, function() {
      input.setButton(buttons.indexOf(selector), true);
    }).bind(stopEvent, function() {
      input.setButton(buttons.indexOf(selector), false);
    }).bind(cancelEvent, function() {
      input.setButton(buttons.indexOf(selector), false);
    });
  });
});<commit_msg>5th<commit_after>var WebNES = function(nes) {
  this.nes = nes;
  window.AudioContext = window.AudioContext||window.webkitAudioContext;
  this.audio = new AudioContext();
  //this.audio = new webkitAudioContext();

  // Initialize screen context
  this.screen = document.getElementById('screen');
  this.canvasContext = this.screen.getContext('2d');
  this.canvasContext.fillStyle = 'black';
  this.canvasContext.fillRect(0, 0, 256, 240);

  // Initialize framebuffer
  this.canvasData = this.canvasContext.getImageData(0, 0, 256, 240);
  for (var i = 3; i < this.canvasData.data.length - 3; i += 4) {
      this.canvasData.data[i] = 0xFF;
  }

  // Unlock audio
  /*var self = this;
  $(document).bind('touchend', function() {
    var source = self.audio.createBufferSource();
    source.buffer = self.audio.createBuffer(1, 44100, 44100);
    source.connect(self.audio.destination);
    if (source.start) {
        source.start(0);
    } else {
        source.noteOn(0);
    }
  });*/

  var intervalId = 0;
  var startEvent = 'touchstart';
  var stopEvent = 'touchend';
  this.screen.addEventListener(startEvent, function() {
    intervalId = setInterval(function() {
      $('#home').slideDown(250);
      $('#portrait_controls').fadeOut(250);
      $('#play').slideUp(250);
      $(document).unbind('touchmove');
      nes.stop();
      clearInterval(intervalId);
    }, 1000); }, false);
  this.screen.addEventListener(stopEvent, function() {
    clearInterval(intervalId);
  }, false);
};

WebNES.prototype = {
  updateStatus: function(status) {
    //console.log('JSNES: ' + status);
  },
  writeFrame: function(buffer, prevBuffer) {
    var data = this.canvasData.data;
    for (var i = 0; i < 256 * 240; i++) {
        var pixel = buffer[i];
        if (pixel != prevBuffer[i]) {
            var j = i * 4;
            data[j] = pixel & 0xFF;
            data[j + 1] = (pixel >> 8) & 0xFF;
            data[j + 2] = (pixel >> 16) & 0xFF;
            prevBuffer[i] = pixel;
        }
    }
    this.canvasContext.putImageData(this.canvasData, 0, 0);
  },
  writeAudio: function(leftSamples, rightSamples) {
    var source = this.audio.createBufferSource();
    var buffer = this.audio.createBuffer(1, leftSamples.length, this.nes.papu.sampleRate);
    buffer.getChannelData(0).set(leftSamples);
    //buffer.getChannelData(1).set(rightSamples);
    source.buffer = buffer;
    source.connect(this.audio.destination);
    if (source.start) {
        source.start(0);
    } else {
        source.noteOn(0);
    }
  }
};

$(function() {
  h = window.screen.availHeight
  w = window.screen.availWidth

  if (h >= 640) {
    $("#portrait_up").css("top", "61%")
    $("#portrait_right").css("top", "70%")
    $("#portrait_down").css("top", "75%")
    $("#portrait_left").css("top", "70%")
    $("#portrait_select").css("top", "84%")
    $("#portrait_start").css("top", "84%")
    $("#portrait_B").css("top", "66%")
    $("#portrait_A").css("top", "60%")
  }

  if(!jQuery.browser.mobile){
    $('#home').hide();
    $('#play').hide();
    $('#desktopLanding').fadeIn(500);
  }

  var db = openDatabase('webnes', '1.0', 'Downloaded NES ROMs', 2 * 1024 * 1024);
  var nes = new JSNES({ 'ui': WebNES, fpsInterval: 2000, emulateSound: true });

  function renderItem(record) {
    var item = $('<li/>').text(record.name).attr('id', record.id);
    var alerted = false;
    var timeoutId = 0;
    var startEvent = 'touchstart';
    var stopEvent = 'touchend';
    item.bind(startEvent, function() {
      alerted = false;
      timeoutId = window.setTimeout(function() {
        alerted = true;
        if (!confirm("Delete this ROM?")) return;
        db.transaction(function(tx){
          tx.executeSql('DELETE FROM roms WHERE id = ?', [record.id], function() {
            localStorage.removeItem(record.storage);
            item.remove();
          });
        });
      }, 1000);
    }).bind(stopEvent, function() {
      clearTimeout(timeoutId);
      if (alerted) return;
      $('#home').slideUp(250);
      $('#play').slideDown(250);
      $('#portrait_controls').slideDown(250);

      if (nes.loadedId !== record.id) {
        var rom = localStorage.getItem(record.storage);
        nes.loadRom(rom);
        nes.loadedId = record.id;
      }
      $(document).bind('touchmove', function(event) {
         event.preventDefault();

		 var presses = [0,0,0,0,0,0,0,0];
  		 var testbuttons = [ 'portrait_A', 'portrait_B', 'portrait_select', 'portrait_start', 'portrait_up', 'portrait_down', 'portrait_left', 'portrait_right' ];
			//nes.input.setButton(3, true);
		 for (var i=0; i < event.originalEvent.targetTouches.length; i++) {
                var x = event.originalEvent.targetTouches[i].pageX - window.pageXOffset;
                var y = event.originalEvent.targetTouches[i].pageY - window.pageYOffset;
                var pressed = document.elementFromPoint(x, y);
				if(pressed){
					if(testbuttons.indexOf(pressed.id) >= 0 && testbuttons.indexOf(pressed.id) < presses.length){
						presses[testbuttons.indexOf(pressed.id)] = 1;
						//nes.input.setButton(testbuttons.indexOf(pressed.id), true);
						//if(!$(pressed).is(selector))
						//{
					      //nes.input.setButton(testbuttons.indexOf(target), true);
					      //nes.input.setButton(testbuttons.indexOf(selector), false);
						//}
					}
				}
   		}

		for (var i=0; i < presses.length; i++) {
			if(presses[i] == 0)
			{
				nes.input.setButton(i, false);
			}
			else
			{
				nes.input.setButton(i, true);
			}
		}
      });
      nes.start();
    });
    return item;
  };

  function addRom(name, url) {
    $.ajax({
      type: 'GET',
      url: url,
      timeout: 3000,
      mimeType: 'text/plain; charset=x-user-defined',
      success: function(data) {
        var key = Math.random().toString(36).slice(2);
        localStorage.setItem(key, data);
        db.transaction(function(tx){
          tx.executeSql('INSERT INTO roms (id, name, storage) VALUES (?, ?, ?)', [null, name, key]);
          tx.executeSql('SELECT * FROM roms WHERE storage = ?', [key], function(tx, result) {
            $('#scroll ul').append(renderItem(result.rows.item(0)));
          });
        });
      }
    });
  }

  db.transaction(function(tx) {
    tx.executeSql('CREATE TABLE IF NOT EXISTS roms (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, storage TEXT)');
    tx.executeSql('SELECT * FROM roms', [], function(tx, result) {
      for (var i = 0; i < result.rows.length; i++) {
        $('#scroll ul').append(renderItem(result.rows.item(i)));
      }
      if (result.rows.length == 0) {
        addRom('Super Mario Bros. 3', 'roms/smb3.nes');
        addRom('Pac-Man', 'roms/Pac-Man.nes');
        addRom('Galaxy Patrol', 'roms/galaxy.nes');
        addRom('Fighter F-8000', 'roms/fighter_f8000.nes');
        addRom('BoxBoy', 'roms/BOXBOY.nes');
      }
    });
  });

  $('#addROM').click(function() {
    Dropbox.choose({
      success: function(files) {
        files.forEach(function(file) {
          addRom(file.name.replace('.nes', ''), file.link);
        });
      },
      linkType: "direct",
      multiselect: true,
      extensions: ['.nes']
    });
  });

  var input = nes.input;
  var buttons = [ '#portrait_A', '#portrait_B', '#portrait_select','#portrait_start', '#portrait_up', '#portrait_down', '#portrait_left', '#portrait_right' ];
  var startEvent = 'touchstart';
  var stopEvent = 'touchend';
  var cancelEvent = 'touchcancel';
  buttons.forEach(function(selector) {
    $(selector).bind(startEvent, function() {
      input.setButton(buttons.indexOf(selector), true);
    }).bind(stopEvent, function() {
      input.setButton(buttons.indexOf(selector), false);
    }).bind(cancelEvent, function() {
      input.setButton(buttons.indexOf(selector), false);
    });
  });
});<|endoftext|>


<commit_before>/**
 * Author: Tony Brix, https://Tony.Brix.ninja
 * Repo: https://github.com/UziTech/jquery.el.js
 * License: MIT
 * Version: 0.1.0
 *
 * simple way to create elements in jquery
 * example:
 * $.el("table#tab1[border=0]").el("tbody").el("tr.odd").el("td{text}");
 */

(function ($) {
	function isStartTokenChar(char) {
		return /[#\.\[\{\s]/.test(char);
	}

	function startNextToken(char) {
		if (/\s/.test(char)) {
			return {
				type: "whitespace"
			};
		}
		switch (char) {
			case null:
				return;
			case "#":
				return {
					type: "id",
					value: ""
				};
			case ".":
				return {
					type: "class",
					value: ""
				};
			case "[":
				return {
					type: "attrs",
					value: ""
				};
			case "{":
				return {
					type: "text",
					value: ""
				};
			default:
				throw new Error("'" + char + "' is not a valid start of a token");
		}
	}

	function parseAttrs(attrs, text) {
		text = text.trim();
		var token = "next";
		var inString = false;
		var inWhitespace = false;
		var attr = {
			name: "",
			value: ""
		};
		for (var i = 0; i <= text.length; i++) {
			var char = null;
			if (i < text.length) {
				char = text[i];
				switch (token) {
					case "name":
						if (char === "=") {
							token = "equals";
							inWhitespace = false;
						} else if (/\s/.test(char)) {
							inWhitespace = true;
						} else if (inWhitespace) {
							// empty attr
							attrs[attr.name] = "";
							attr = {
								name: "",
								value: ""
							};
							inWhitespace = false;
							token = "name";
							if (/[\w-]/.test(char)) {
								attr.name += char;
							} else {
								throw new Error("'" + char + "' is not a valid attribute name character");
							}
						} else if (/[\w-]/.test(char)) {
							attr.name += char;
						} else {
							throw new Error("'" + char + "' is not a valid attribute name character");
						}
						break;
					case "equals":
						if (/\s/.test(char)) {
							// ignore whitespace
						} else if (/["']/.test(char)) {
							inString = char;
							token = "value";
						} else {
							attr.value += char;
							token = "value";
						}
						break;
					case "value":
						if (inString) {
							if (char === "\\") {
								if (text.length > i + 1) {
									var nextInStringChar = text[i + 1];
									switch (nextInStringChar) {
										case "\\":
										case "'":
										case "\"":
											attr.value += nextInStringChar;
											i++;
											break;
										default:
											attr.value += char;
									}
								} else {
									attr.value += char;
								}
							} else if (char === inString) {
								inString = false;
								attrs[attr.name] = attr.value;
								attr = {
									name: "",
									value: ""
								};
								token = "next";
							} else {
								attr.value += char;
							}
						} else {
							if (char === "\\") {
								if (text.length > i + 1) {
									var nextChar = text[i + 1];
									switch (nextChar) {
										case "\\":
											attr.value += nextChar;
											i++;
											break;
										default:
											attr.value += char;
									}
								} else {
									attr.value += char;
								}
							} else if (/\s/.test(char)) {
								attrs[attr.name] = attr.value;
								attr = {
									name: "",
									value: ""
								};
								token = "next";
							} else {
								attr.value += char;
							}
						}
						break;
					case "next":
						if (/[\w-]/.test(char)) {
							inWhitespace = false;
							attr.name += char;
							token = "name";
						} else if (/[^\s]/.test(char)) {
							throw new Error("'" + char + "' is not a valid attribute name character");
						}
						break;
					default:
						// this should never happen
						throw new Error("'" + token + "' is an invalid token");
				}
			} else {
				if (attr.name) {
					attrs[attr.name] = attr.value;
				}
			}
		}
		return attrs;
	}

	function parseText(text) {
		var el = {
			tag: "div",
			id: null,
			classes: [],
			attrs: {},
			text: ""
		};
		var token = {
			type: "tag",
			value: "",
		};
		for (var i = 0; i <= text.length; i++) {

			var char = null;
			if (i < text.length) {
				char = text[i];
			}
			switch (token.type) {
				case "whitespace":
					if (char && /\S/.test(char)) {
						token = startNextToken(char);
					}
					break;
				case "tag":
					if (char === "\\") {
						if (text.length > i + 1) {
							var nextTagChar = text[i + 1];
							if (nextTagChar === "\\" || (isStartTokenChar(nextTagChar) && /\S/.test(nextTagChar))) {
								token.value += nextTagChar;
								i++;
							} else {
								token.value += char;
							}
						} else {
							token.value += char;
						}
					} else if (char && !isStartTokenChar(char)) {
						token.value += char;
					} else {
						if (token.value) {
							if (/^[0-9]/.test(token.value)) {
								throw new Error("Tag cannot start with a number.");
							}
							el.tag = token.value;
						}
						token = startNextToken(char);
					}
					break;
				case "id":
					if (char === "\\") {
						if (text.length > i + 1) {
							var nextIdChar = text[i + 1];
							if (nextIdChar === "\\" || (isStartTokenChar(nextIdChar) && /\S/.test(nextIdChar))) {
								token.value += nextIdChar;
								i++;
							} else {
								token.value += char;
							}
						} else {
							token.value += char;
						}
					} else if (char && !isStartTokenChar(char)) {
						token.value += char;
					} else {
						if (!token.value) {
							if (char === null) {
								throw new Error("id cannot be empty");
							} else {
								throw new Error("'" + char + "' is not a valid id character");
							}
						}
						if (el.id) {
							throw new Error("cannot have multiple ids");
						} else {
							el.id = token.value;
							token = startNextToken(char);
						}
					}
					break;
				case "class":
					if (char === "\\") {
						if (text.length > i + 1) {
							var nextClassChar = text[i + 1];
							if (nextClassChar === "\\" || (isStartTokenChar(nextClassChar) && /\S/.test(nextClassChar))) {
								token.value += nextClassChar;
								i++;
							} else {
								token.value += char;
							}
						} else {
							token.value += char;
						}
					} else if (char && !isStartTokenChar(char)) {
						token.value += char;
					} else {
						if (!token.value) {
							if (char === null) {
								throw new Error("class cannot be empty");
							} else {
								throw new Error("'" + char + "' is not a valid class character");
							}
						}
						el.classes.push(token.value);
						token = startNextToken(char);
					}
					break;
				case "attrs":
					if (char === null) {
						throw new Error("missing ']' at end of attributes token");
					} else if (char === "\\") {
						if (text.length > i + 1) {
							var nextAttrChar = text[i + 1];
							switch (nextAttrChar) {
								case "\\":
									token.value += "\\\\";
									i++;
									break;
								case "]":
									token.value += nextAttrChar;
									i++;
									break;
								default:
									token.value += char;
							}
						} else {
							token.value += char;
						}
					} else if (/[^\]]/.test(char)) {
						token.value += char;
					} else {
						parseAttrs(el.attrs, token.value);
						token = {
							type: "next"
						};
					}
					break;
				case "text":
					if (char === null) {
						throw new Error("missing '}' at end of text token");
					} else if (char === "\\") {
						if (text.length > i + 1) {
							var nextTextChar = text[i + 1];
							switch (nextTextChar) {
								case "\\":
								case "}":
									token.value += nextTextChar;
									i++;
									break;
								default:
									token.value += char;
							}
						} else {
							token.value += char;
						}
					} else if (/[^}]/.test(char)) {
						token.value += char;
					} else {
						el.text += token.value;
						token = {
							type: "next"
						};
					}
					break;
				case "next":
					if (char) {
						token = startNextToken(char);
					}
					break;
				default:
					// this should never happen
					throw new Error("'" + token.type + "' is not a valid token type");
			}
		}
		return el;
	}

	$.el = function (text) {
		if (typeof text === "undefined" || text === null) {
			text = "";
		} else if (typeof text === "function") {
			text = text.call(this);
		}
		// TODO: object
		text = ("" + text).trim();
		var el = parseText(text);
		var $el = $("<" + el.tag + " />");
		if (el.id) {
			$el.attr({ id: el.id });
		}
		if (el.classes.length > 0) {
			$el.addClass(el.classes.join(" "));
		}
		if (el.attrs) {
			$el.attr(el.attrs);
		}
		if (el.text) {
			$el.text(el.text);
		}
		return $el;
	};

	$.fn.el = function (text, returnParent) {
		var $el = $.el.call(this, text).appendTo(this);
		if (returnParent) {
			return this;
		}
		return $el;
	};
})(jQuery);
<commit_msg>fix class and id empty check<commit_after>/**
 * Author: Tony Brix, https://Tony.Brix.ninja
 * Repo: https://github.com/UziTech/jquery.el.js
 * License: MIT
 * Version: 0.1.0
 *
 * simple way to create elements in jquery
 * example:
 * $.el("table#tab1[border=0]").el("tbody").el("tr.odd").el("td{text}");
 */

(function ($) {
	function isStartTokenChar(char) {
		return /[#\.\[\{\s]/.test(char);
	}

	function startNextToken(char) {
		if (/\s/.test(char)) {
			return {
				type: "whitespace"
			};
		}
		switch (char) {
			case null:
				return;
			case "#":
				return {
					type: "id",
					value: ""
				};
			case ".":
				return {
					type: "class",
					value: ""
				};
			case "[":
				return {
					type: "attrs",
					value: ""
				};
			case "{":
				return {
					type: "text",
					value: ""
				};
			default:
				throw new Error("'" + char + "' is not a valid start of a token");
		}
	}

	function parseAttrs(attrs, text) {
		text = text.trim();
		var token = "next";
		var inString = false;
		var inWhitespace = false;
		var attr = {
			name: "",
			value: ""
		};
		for (var i = 0; i <= text.length; i++) {
			var char = null;
			if (i < text.length) {
				char = text[i];
				switch (token) {
					case "name":
						if (char === "=") {
							token = "equals";
							inWhitespace = false;
						} else if (/\s/.test(char)) {
							inWhitespace = true;
						} else if (inWhitespace) {
							// empty attr
							attrs[attr.name] = "";
							attr = {
								name: "",
								value: ""
							};
							inWhitespace = false;
							token = "name";
							if (/[\w-]/.test(char)) {
								attr.name += char;
							} else {
								throw new Error("'" + char + "' is not a valid attribute name character");
							}
						} else if (/[\w-]/.test(char)) {
							attr.name += char;
						} else {
							throw new Error("'" + char + "' is not a valid attribute name character");
						}
						break;
					case "equals":
						if (/\s/.test(char)) {
							// ignore whitespace
						} else if (/["']/.test(char)) {
							inString = char;
							token = "value";
						} else {
							attr.value += char;
							token = "value";
						}
						break;
					case "value":
						if (inString) {
							if (char === "\\") {
								if (text.length > i + 1) {
									var nextInStringChar = text[i + 1];
									switch (nextInStringChar) {
										case "\\":
										case "'":
										case "\"":
											attr.value += nextInStringChar;
											i++;
											break;
										default:
											attr.value += char;
									}
								} else {
									attr.value += char;
								}
							} else if (char === inString) {
								inString = false;
								attrs[attr.name] = attr.value;
								attr = {
									name: "",
									value: ""
								};
								token = "next";
							} else {
								attr.value += char;
							}
						} else {
							if (char === "\\") {
								if (text.length > i + 1) {
									var nextChar = text[i + 1];
									switch (nextChar) {
										case "\\":
											attr.value += nextChar;
											i++;
											break;
										default:
											attr.value += char;
									}
								} else {
									attr.value += char;
								}
							} else if (/\s/.test(char)) {
								attrs[attr.name] = attr.value;
								attr = {
									name: "",
									value: ""
								};
								token = "next";
							} else {
								attr.value += char;
							}
						}
						break;
					case "next":
						if (/[\w-]/.test(char)) {
							inWhitespace = false;
							attr.name += char;
							token = "name";
						} else if (/[^\s]/.test(char)) {
							throw new Error("'" + char + "' is not a valid attribute name character");
						}
						break;
					default:
						// this should never happen
						throw new Error("'" + token + "' is an invalid token");
				}
			} else {
				if (attr.name) {
					attrs[attr.name] = attr.value;
				}
			}
		}
		return attrs;
	}

	function parseText(text) {
		var el = {
			tag: "div",
			id: null,
			classes: [],
			attrs: {},
			text: ""
		};
		var token = {
			type: "tag",
			value: "",
		};
		for (var i = 0; i <= text.length; i++) {

			var char = null;
			if (i < text.length) {
				char = text[i];
			}
			switch (token.type) {
				case "whitespace":
					if (char && /\S/.test(char)) {
						token = startNextToken(char);
					}
					break;
				case "tag":
					if (char === "\\") {
						if (text.length > i + 1) {
							var nextTagChar = text[i + 1];
							if (nextTagChar === "\\" || (isStartTokenChar(nextTagChar) && /\S/.test(nextTagChar))) {
								token.value += nextTagChar;
								i++;
							} else {
								token.value += char;
							}
						} else {
							token.value += char;
						}
					} else if (char && !isStartTokenChar(char)) {
						token.value += char;
					} else {
						if (token.value) {
							if (/^[0-9]/.test(token.value)) {
								throw new Error("Tag cannot start with a number.");
							}
							el.tag = token.value;
						}
						token = startNextToken(char);
					}
					break;
				case "id":
					if (char === "\\") {
						if (text.length > i + 1) {
							var nextIdChar = text[i + 1];
							if (nextIdChar === "\\" || (isStartTokenChar(nextIdChar) && /\S/.test(nextIdChar))) {
								token.value += nextIdChar;
								i++;
							} else {
								token.value += char;
							}
						} else {
							token.value += char;
						}
					} else if (char && !isStartTokenChar(char)) {
						token.value += char;
					} else {
						if (!token.value) {
							throw new Error("id cannot be empty");
						}
						if (el.id) {
							throw new Error("cannot have multiple ids");
						} else {
							el.id = token.value;
							token = startNextToken(char);
						}
					}
					break;
				case "class":
					if (char === "\\") {
						if (text.length > i + 1) {
							var nextClassChar = text[i + 1];
							if (nextClassChar === "\\" || (isStartTokenChar(nextClassChar) && /\S/.test(nextClassChar))) {
								token.value += nextClassChar;
								i++;
							} else {
								token.value += char;
							}
						} else {
							token.value += char;
						}
					} else if (char && !isStartTokenChar(char)) {
						token.value += char;
					} else {
						if (!token.value) {
							throw new Error("class cannot be empty");
						}
						el.classes.push(token.value);
						token = startNextToken(char);
					}
					break;
				case "attrs":
					if (char === null) {
						throw new Error("missing ']' at end of attributes token");
					} else if (char === "\\") {
						if (text.length > i + 1) {
							var nextAttrChar = text[i + 1];
							switch (nextAttrChar) {
								case "\\":
									token.value += "\\\\";
									i++;
									break;
								case "]":
									token.value += nextAttrChar;
									i++;
									break;
								default:
									token.value += char;
							}
						} else {
							token.value += char;
						}
					} else if (/[^\]]/.test(char)) {
						token.value += char;
					} else {
						parseAttrs(el.attrs, token.value);
						token = {
							type: "next"
						};
					}
					break;
				case "text":
					if (char === null) {
						throw new Error("missing '}' at end of text token");
					} else if (char === "\\") {
						if (text.length > i + 1) {
							var nextTextChar = text[i + 1];
							switch (nextTextChar) {
								case "\\":
								case "}":
									token.value += nextTextChar;
									i++;
									break;
								default:
									token.value += char;
							}
						} else {
							token.value += char;
						}
					} else if (/[^}]/.test(char)) {
						token.value += char;
					} else {
						el.text += token.value;
						token = {
							type: "next"
						};
					}
					break;
				case "next":
					if (char) {
						token = startNextToken(char);
					}
					break;
				default:
					// this should never happen
					throw new Error("'" + token.type + "' is not a valid token type");
			}
		}
		return el;
	}

	$.el = function (text) {
		if (typeof text === "undefined" || text === null) {
			text = "";
		} else if (typeof text === "function") {
			text = text.call(this);
		}
		// TODO: object
		text = ("" + text).trim();
		var el = parseText(text);
		var $el = $("<" + el.tag + " />");
		if (el.id) {
			$el.attr({ id: el.id });
		}
		if (el.classes.length > 0) {
			$el.addClass(el.classes.join(" "));
		}
		if (el.attrs) {
			$el.attr(el.attrs);
		}
		if (el.text) {
			$el.text(el.text);
		}
		return $el;
	};

	$.fn.el = function (text, returnParent) {
		var $el = $.el.call(this, text).appendTo(this);
		if (returnParent) {
			return this;
		}
		return $el;
	};
})(jQuery);
<|endoftext|>


<commit_before>/* global config, vendor, MM, Log, Module */
/* Magic Mirror
 * Module and File loaders.
 *
 * By Michael Teeuw http://michaelteeuw.nl
 * MIT Licensed.
 */

var Loader = (function() {

	/* Create helper valiables */

	var loadedModuleFiles = [];
	var loadedFiles = [];
	var moduleObjects = [];

	/* Private Methods */

	/* loadModules()
	 * Loops thru all modules and requests load for every module.
	 */
	var loadModules = function() {

		var moduleData = getModuleData();

		var loadNextModule = function() {
			if (moduleData.length > 0) {
				var nextModule = moduleData[0];
				loadModule(nextModule, function() {
					moduleData = moduleData.slice(1);
					loadNextModule();
				});
			} else {
				// All modules loaded. Load custom.css
				// This is done after all the moduels so we can
				// overwrite all the defined styls.

				loadFile("css/custom.css", function() {
					// custom.css loaded. Start all modules.
					startModules();
				});

			}
		};

		loadNextModule();
	};

	/* startModules()
	 * Loops thru all modules and requests start for every module.
	 */
	var startModules = function() {
		for (var m in moduleObjects) {
			var module = moduleObjects[m];
			module.start();
		}

		// Notifiy core of loded modules.
		MM.modulesStarted(moduleObjects);
	};

	/* getAllModules()
	 * Retrieve list of all modules.
	 *
	 * return array - module data as configured in config
	 */
	var getAllModules = function() {
		return config.modules;
	};

	/* getModuleData()
	 * Generate array with module information including module paths.
	 *
	 * return array - Module information.
	 */
	var getModuleData = function() {
		var modules = getAllModules();
		var moduleFiles = [];

		for (var m in modules) {
			var moduleData = modules[m];
			var module = moduleData.module;

			var elements = module.split("/");
			var moduleName = elements[elements.length - 1];
			var moduleFolder =  config.paths.modules + "/" + module;

			if (defaultModules.indexOf(moduleName) !== -1) {
				moduleFolder =  config.paths.modules + "/default/" + module;
			}

			if (moduleData.disabled === true) {
				continue;
			}

			moduleFiles.push({
				index: m,
				identifier: "module_" + m + "_" + module,
				name: moduleName,
				path: moduleFolder + "/" ,
				file: moduleName + ".js",
				position: moduleData.position,
				header: moduleData.header,
				config: moduleData.config,
				classes: (typeof moduleData.classes !== "undefined") ? moduleData.classes + " " + module : module
			});

		}

		return moduleFiles;
	};

	/* loadModule(module)
	 * Load modules via ajax request and create module objects.
	 *
	 * argument callback function - Function called when done.
	 * argument module object - Information about the module we want to load.
	 */
	var loadModule = function(module, callback) {
		var url = module.path + "/" + module.file;

		var afterLoad = function() {
			var moduleObject = Module.create(module.name);
			if (moduleObject) {
				bootstrapModule(module, moduleObject, function() {
					callback();
				});
			} else {
				callback();
			}
		};

		if (loadedModuleFiles.indexOf(url) !== -1) {
			afterLoad();
		} else {
			loadFile(url, function() {
				loadedModuleFiles.push(url);
				afterLoad();
			});
		}

	};

	/* bootstrapModule(module, mObj)
	 * Bootstrap modules by setting the module data and loading the scripts & styles.
	 *
	 * argument module object - Information about the module we want to load.
	 * argument mObj object - Modules instance.
	 * argument callback function - Function called when done.
	 */
	var bootstrapModule = function(module, mObj, callback) {
		Log.info("Bootstrapping module: " + module.name);

		mObj.setData(module);

		mObj.loadScripts(function() {
			Log.log("Scripts loaded for: " + module.name);
			mObj.loadStyles(function() {
				Log.log("Styles loaded for: " + module.name);
				mObj.loadTranslations(function() {
					Log.log("Translations loaded for: " + module.name);
					moduleObjects.push(mObj);
					callback();
				});
			});
		});

	};

	/* loadFile(fileName)
	 * Load a script or stylesheet by adding it to the dom.
	 *
	 * argument fileName string - Path of the file we want to load.
	 * argument callback function - Function called when done.
	 */
	var loadFile = function(fileName, callback) {

		var extension =  fileName.slice((Math.max(0, fileName.lastIndexOf(".")) || Infinity) + 1);

		switch (extension.toLowerCase()) {
		case "js":
			Log.log("Load script: " + fileName);
			var script = document.createElement("script");
			script.type = "text/javascript";
			script.src = fileName;
			script.onload = function() {
				if (typeof callback === "function") {callback();}
			};
			document.getElementsByTagName("body")[0].appendChild(script);
			break;
		case "css":
			Log.log("Load stylesheet: " + fileName);
			var stylesheet = document.createElement("link");
			stylesheet.rel = "stylesheet";
			stylesheet.type = "text/css";
			stylesheet.href = fileName;
			stylesheet.onload = function() {
				if (typeof callback === "function") {callback();}
			};
			document.getElementsByTagName("head")[0].appendChild(stylesheet);
			break;
		}

	};

	/* Public Methods */
	return {

		/* loadModules()
		 * Load all modules as defined in the config.
		 */
		loadModules: function() {
			loadModules();
		},

		/* loadFile()
		 * Load a file (script or stylesheet).
		 * Prevent double loading and search for files in the vendor folder.
		 *
		 * argument fileName string - Path of the file we want to load.
		 * argument module Module Object - the module that calls the loadFile function.
		 * argument callback function - Function called when done.
		 */
		loadFile: function(fileName, module, callback) {

			if (loadedFiles.indexOf(fileName.toLowerCase()) !== -1) {
				Log.log("File already loaded: " + fileName);
				callback();
				return;
			}

			if (fileName.indexOf("http://") === 0 || fileName.indexOf("https://") === 0 || fileName.indexOf("/") !== -1) {
				// This is an absolute or relative path.
				// Load it and then return.
				loadedFiles.push(fileName.toLowerCase());
				loadFile(fileName, callback);
				return;
			}

			if (vendor[fileName] !== undefined) {
				// This file is available in the vendor folder.
				// Load it from this vendor folder.
				loadedFiles.push(fileName.toLowerCase());
				loadFile(config.paths.vendor + "/" + vendor[fileName], callback);
				return;
			}

			// File not loaded yet.
			// Load it based on the module path.
			loadedFiles.push(fileName.toLowerCase());
			loadFile(module.file(fileName), callback);
		}
	};

})();
<commit_msg>Prevent crash system if cannot load a js script or style sheet.<commit_after>/* global config, vendor, MM, Log, Module */
/* Magic Mirror
 * Module and File loaders.
 *
 * By Michael Teeuw http://michaelteeuw.nl
 * MIT Licensed.
 */

var Loader = (function() {

	/* Create helper valiables */

	var loadedModuleFiles = [];
	var loadedFiles = [];
	var moduleObjects = [];

	/* Private Methods */

	/* loadModules()
	 * Loops thru all modules and requests load for every module.
	 */
	var loadModules = function() {

		var moduleData = getModuleData();

		var loadNextModule = function() {
			if (moduleData.length > 0) {
				var nextModule = moduleData[0];
				loadModule(nextModule, function() {
					moduleData = moduleData.slice(1);
					loadNextModule();
				});
			} else {
				// All modules loaded. Load custom.css
				// This is done after all the moduels so we can
				// overwrite all the defined styls.

				loadFile("css/custom.css", function() {
					// custom.css loaded. Start all modules.
					startModules();
				});

			}
		};

		loadNextModule();
	};

	/* startModules()
	 * Loops thru all modules and requests start for every module.
	 */
	var startModules = function() {
		for (var m in moduleObjects) {
			var module = moduleObjects[m];
			module.start();
		}

		// Notifiy core of loded modules.
		MM.modulesStarted(moduleObjects);
	};

	/* getAllModules()
	 * Retrieve list of all modules.
	 *
	 * return array - module data as configured in config
	 */
	var getAllModules = function() {
		return config.modules;
	};

	/* getModuleData()
	 * Generate array with module information including module paths.
	 *
	 * return array - Module information.
	 */
	var getModuleData = function() {
		var modules = getAllModules();
		var moduleFiles = [];

		for (var m in modules) {
			var moduleData = modules[m];
			var module = moduleData.module;

			var elements = module.split("/");
			var moduleName = elements[elements.length - 1];
			var moduleFolder =  config.paths.modules + "/" + module;

			if (defaultModules.indexOf(moduleName) !== -1) {
				moduleFolder =  config.paths.modules + "/default/" + module;
			}

			if (moduleData.disabled === true) {
				continue;
			}

			moduleFiles.push({
				index: m,
				identifier: "module_" + m + "_" + module,
				name: moduleName,
				path: moduleFolder + "/" ,
				file: moduleName + ".js",
				position: moduleData.position,
				header: moduleData.header,
				config: moduleData.config,
				classes: (typeof moduleData.classes !== "undefined") ? moduleData.classes + " " + module : module
			});

		}

		return moduleFiles;
	};

	/* loadModule(module)
	 * Load modules via ajax request and create module objects.
	 *
	 * argument callback function - Function called when done.
	 * argument module object - Information about the module we want to load.
	 */
	var loadModule = function(module, callback) {
		var url = module.path + "/" + module.file;

		var afterLoad = function() {
			var moduleObject = Module.create(module.name);
			if (moduleObject) {
				bootstrapModule(module, moduleObject, function() {
					callback();
				});
			} else {
				callback();
			}
		};

		if (loadedModuleFiles.indexOf(url) !== -1) {
			afterLoad();
		} else {
			loadFile(url, function() {
				loadedModuleFiles.push(url);
				afterLoad();
			});
		}

	};

	/* bootstrapModule(module, mObj)
	 * Bootstrap modules by setting the module data and loading the scripts & styles.
	 *
	 * argument module object - Information about the module we want to load.
	 * argument mObj object - Modules instance.
	 * argument callback function - Function called when done.
	 */
	var bootstrapModule = function(module, mObj, callback) {
		Log.info("Bootstrapping module: " + module.name);

		mObj.setData(module);

		mObj.loadScripts(function() {
			Log.log("Scripts loaded for: " + module.name);
			mObj.loadStyles(function() {
				Log.log("Styles loaded for: " + module.name);
				mObj.loadTranslations(function() {
					Log.log("Translations loaded for: " + module.name);
					moduleObjects.push(mObj);
					callback();
				});
			});
		});

	};

	/* loadFile(fileName)
	 * Load a script or stylesheet by adding it to the dom.
	 *
	 * argument fileName string - Path of the file we want to load.
	 * argument callback function - Function called when done.
	 */
	var loadFile = function(fileName, callback) {

		var extension =  fileName.slice((Math.max(0, fileName.lastIndexOf(".")) || Infinity) + 1);

		switch (extension.toLowerCase()) {
		case "js":
			Log.log("Load script: " + fileName);
			var script = document.createElement("script");
			script.type = "text/javascript";
			script.src = fileName;
			script.onload = function() {
				if (typeof callback === "function") {callback();}
			};
			script.onerror = function() {
				console.error("Error on loading script:", fileName);
				if (typeof callback === "function") {callback();}
			};

			document.getElementsByTagName("body")[0].appendChild(script);
			break;
		case "css":
			Log.log("Load stylesheet: " + fileName);
			var stylesheet = document.createElement("link");
			stylesheet.rel = "stylesheet";
			stylesheet.type = "text/css";
			stylesheet.href = fileName;
			stylesheet.onload = function() {
				if (typeof callback === "function") {callback();}
			};
			stylesheet.onerror = function() {
				console.error("Error on loading stylesheet:", fileName);
				if (typeof callback === "function") {callback();}
			};

			document.getElementsByTagName("head")[0].appendChild(stylesheet);
			break;
		}

	};

	/* Public Methods */
	return {

		/* loadModules()
		 * Load all modules as defined in the config.
		 */
		loadModules: function() {
			loadModules();
		},

		/* loadFile()
		 * Load a file (script or stylesheet).
		 * Prevent double loading and search for files in the vendor folder.
		 *
		 * argument fileName string - Path of the file we want to load.
		 * argument module Module Object - the module that calls the loadFile function.
		 * argument callback function - Function called when done.
		 */
		loadFile: function(fileName, module, callback) {

			if (loadedFiles.indexOf(fileName.toLowerCase()) !== -1) {
				Log.log("File already loaded: " + fileName);
				callback();
				return;
			}

			if (fileName.indexOf("http://") === 0 || fileName.indexOf("https://") === 0 || fileName.indexOf("/") !== -1) {
				// This is an absolute or relative path.
				// Load it and then return.
				loadedFiles.push(fileName.toLowerCase());
				loadFile(fileName, callback);
				return;
			}

			if (vendor[fileName] !== undefined) {
				// This file is available in the vendor folder.
				// Load it from this vendor folder.
				loadedFiles.push(fileName.toLowerCase());
				loadFile(config.paths.vendor + "/" + vendor[fileName], callback);
				return;
			}

			// File not loaded yet.
			// Load it based on the module path.
			loadedFiles.push(fileName.toLowerCase());
			loadFile(module.file(fileName), callback);
		}
	};

})();
<|endoftext|>


<commit_before>import Accordion from './react-chayns-accordion/component/Accordion';
import AccordionIntro from './react-chayns-accordion/component/AccordionIntro';
import AmountControl from './react-chayns-amountcontrol/component/AmountControl';

import Badge from './react-chayns-badge/component/Badge';
import Button from './react-chayns-button/component/Button';
import Bubble from './react-chayns-bubble/component/Bubble';
import ChooseButton from './react-chayns-button/component/ChooseButton';

import Calendar from './react-chayns-calendar/component/Calendar';
import Checkbox from './react-chayns-checkbox/component/Checkbox';
import ComboBox from './react-chayns-combobox/component/ComboBox';
import ContextMenu from './react-chayns-contextmenu/component/ContextMenu';

import EmojiInput from './react-chayns-emoji_input/component/EmojiInput';

import ExpandableContent from './react-chayns-expandable_content/component/ExpandableContent';

import FormattedInput from './react-chayns-formatted_input/component/FormattedInput';
import {
    Formatter,
    IntegerFormatter,
    DecimalFormatter,
    PriceFormatter,
    FORMAT_INTEGER,
    FORMAT_DECIMAL,
    FORMAT_PRICE,
} from './react-chayns-formatted_input/utils/index';

import Gallery from './react-chayns-gallery/component/Gallery';
import GridCalendar from './react-chayns-gridcalendar/component/GridCalendar';

import Icon from './react-chayns-icon/component/Icon';
import Input from './react-chayns-input/component/Input';

import List from './react-chayns-list/component/List';
import ListItem from './react-chayns-list/component/ListItem';

import ModeSwitch from './react-chayns-modeswitch/component/ModeSwitch';
import Mode from './react-chayns-modeswitch/component/Mode';

import OpeningTimes from './react-chayns-openingtimes/component/OpeningTimes';

import PersonFinder from './react-chayns-personfinder/component/PersonFinder';

import RadioButton from './react-chayns-radiobutton/component/RadioButton';
import ReceiverInput from './react-chayns-receiverinput/component/ReceiverInput';
import RfidInput from './react-chayns-rfid_input/component/RfidInput';

import ScrollView from './react-chayns-scrollview/component/ScrollView';
import SelectButton from './react-chayns-selectbutton/component/SelectButton';
import SelectList from './react-chayns-selectlist/component/SelectList';
import SelectListItem from './react-chayns-selectlist/component/SelectItem';
import SetupWizard from './react-chayns-setupwizard/component/SetupWizard';
import SetupWizardItem from './react-chayns-setupwizard/component/SetupItem';

import SharingBar from './react-chayns-sharingbar/component/SharingBar';
import SmallWaitCursor from './react-chayns-smallwaitcursor/component/SmallWaitCursor';

import TextArea from './react-chayns-textarea/component/TextArea';
import TextString from './react-chayns-textstring/component/TextString';
import Tooltip from './react-chayns-tooltip/component/Tooltip';

import FileInput from './react-chayns-file_input/component/FileInput';
import Map from './react-chayns-map/component/Map';

import resolveAbsoluteImport from './utils/babel/resolveAbsoluteImport';
import OrientationHelper from './utils/OrientationHelper';
import imageUpload from './utils/imageUpload';

import { CHAYNS_CSS_VERSION } from './constants';

export {
    Accordion,
    AccordionIntro,
    AmountControl,
    Badge,
    Bubble,
    Button,
    ChooseButton,
    Calendar,
    Checkbox,
    ComboBox,
    ContextMenu,
    EmojiInput,
    ExpandableContent,
    FormattedInput,
    Formatter,
    IntegerFormatter,
    DecimalFormatter,
    PriceFormatter,
    Gallery,
    GridCalendar,
    Icon,
    Input,
    List,
    ListItem,
    ModeSwitch,
    Mode,
    OpeningTimes,
    OrientationHelper,
    PersonFinder,
    RadioButton,
    ReceiverInput,
    RfidInput,
    ScrollView,
    SelectButton,
    SelectList,
    SelectListItem,
    SetupWizard,
    SetupWizardItem,
    SharingBar,
    SmallWaitCursor,
    TextArea,
    TextString,
    Tooltip,
    FileInput,
    Map,
    resolveAbsoluteImport,
    imageUpload,
    CHAYNS_CSS_VERSION,
    FORMAT_INTEGER,
    FORMAT_DECIMAL,
    FORMAT_PRICE,
};
<commit_msg>:wrench: Export isTobitEmployee<commit_after>import Accordion from './react-chayns-accordion/component/Accordion';
import AccordionIntro from './react-chayns-accordion/component/AccordionIntro';
import AmountControl from './react-chayns-amountcontrol/component/AmountControl';

import Badge from './react-chayns-badge/component/Badge';
import Button from './react-chayns-button/component/Button';
import Bubble from './react-chayns-bubble/component/Bubble';
import ChooseButton from './react-chayns-button/component/ChooseButton';

import Calendar from './react-chayns-calendar/component/Calendar';
import Checkbox from './react-chayns-checkbox/component/Checkbox';
import ComboBox from './react-chayns-combobox/component/ComboBox';
import ContextMenu from './react-chayns-contextmenu/component/ContextMenu';

import EmojiInput from './react-chayns-emoji_input/component/EmojiInput';

import ExpandableContent from './react-chayns-expandable_content/component/ExpandableContent';

import FormattedInput from './react-chayns-formatted_input/component/FormattedInput';
import {
    Formatter,
    IntegerFormatter,
    DecimalFormatter,
    PriceFormatter,
    FORMAT_INTEGER,
    FORMAT_DECIMAL,
    FORMAT_PRICE,
} from './react-chayns-formatted_input/utils/index';

import Gallery from './react-chayns-gallery/component/Gallery';
import GridCalendar from './react-chayns-gridcalendar/component/GridCalendar';

import Icon from './react-chayns-icon/component/Icon';
import Input from './react-chayns-input/component/Input';

import List from './react-chayns-list/component/List';
import ListItem from './react-chayns-list/component/ListItem';

import ModeSwitch from './react-chayns-modeswitch/component/ModeSwitch';
import Mode from './react-chayns-modeswitch/component/Mode';

import OpeningTimes from './react-chayns-openingtimes/component/OpeningTimes';

import PersonFinder from './react-chayns-personfinder/component/PersonFinder';

import RadioButton from './react-chayns-radiobutton/component/RadioButton';
import ReceiverInput from './react-chayns-receiverinput/component/ReceiverInput';
import RfidInput from './react-chayns-rfid_input/component/RfidInput';

import ScrollView from './react-chayns-scrollview/component/ScrollView';
import SelectButton from './react-chayns-selectbutton/component/SelectButton';
import SelectList from './react-chayns-selectlist/component/SelectList';
import SelectListItem from './react-chayns-selectlist/component/SelectItem';
import SetupWizard from './react-chayns-setupwizard/component/SetupWizard';
import SetupWizardItem from './react-chayns-setupwizard/component/SetupItem';

import SharingBar from './react-chayns-sharingbar/component/SharingBar';
import SmallWaitCursor from './react-chayns-smallwaitcursor/component/SmallWaitCursor';

import TextArea from './react-chayns-textarea/component/TextArea';
import TextString from './react-chayns-textstring/component/TextString';
import Tooltip from './react-chayns-tooltip/component/Tooltip';

import FileInput from './react-chayns-file_input/component/FileInput';
import Map from './react-chayns-map/component/Map';

import resolveAbsoluteImport from './utils/babel/resolveAbsoluteImport';
import OrientationHelper from './utils/OrientationHelper';
import imageUpload from './utils/imageUpload';
import isTobitEmployee from './utils/tobitEmployee';

import { CHAYNS_CSS_VERSION } from './constants';

export {
    Accordion,
    AccordionIntro,
    AmountControl,
    Badge,
    Bubble,
    Button,
    ChooseButton,
    Calendar,
    Checkbox,
    ComboBox,
    ContextMenu,
    EmojiInput,
    ExpandableContent,
    FormattedInput,
    Formatter,
    IntegerFormatter,
    DecimalFormatter,
    PriceFormatter,
    Gallery,
    GridCalendar,
    Icon,
    Input,
    List,
    ListItem,
    ModeSwitch,
    Mode,
    OpeningTimes,
    OrientationHelper,
    PersonFinder,
    RadioButton,
    ReceiverInput,
    RfidInput,
    ScrollView,
    SelectButton,
    SelectList,
    SelectListItem,
    SetupWizard,
    SetupWizardItem,
    SharingBar,
    SmallWaitCursor,
    TextArea,
    TextString,
    Tooltip,
    FileInput,
    Map,
    resolveAbsoluteImport,
    imageUpload,
    isTobitEmployee,
    CHAYNS_CSS_VERSION,
    FORMAT_INTEGER,
    FORMAT_DECIMAL,
    FORMAT_PRICE,
};
<|endoftext|>


<commit_before>// Copyright 2019, University of Colorado Boulder

/**
 * An action that can be executed and sent to the PhET-iO data stream, and optionally recorded for playback. This type
 * will also validate the argument types passed to the action function.
 *
 * @author Sam Reid (PhET Interactive Simulations)
 * @author Michael Kauzmann (PhET Interactive Simulations)
 */
define( require => {
  'use strict';

  // modules
  const ActionIO = require( 'AXON/ActionIO' );
  const axon = require( 'AXON/axon' );
  const PhetioObject = require( 'TANDEM/PhetioObject' );
  const Tandem = require( 'TANDEM/Tandem' );
  const ValidatorDef = require( 'AXON/ValidatorDef' );
  const validate = require( 'AXON/validate' );

  // constants
  // Simulations have thousands of Emitters, so we re-use objects where possible.
  const EMPTY_ARRAY = [];
  assert && Object.freeze( EMPTY_ARRAY );

  // TODO: Map that holds all the ActionIO types created so they can be reused when possible, https://github.com/phetsims/axon/issues/257
  const TYPE_IO_MAP = {};

  // allowed keys to options.parameters
  const PARAMETER_KEYS = [
    'name', // {string}
    'phetioType', // {function(new:ObjectIO)}
    'phetioDocumentation' // {string}
  ].concat( ValidatorDef.VALIDATOR_KEYS );

  class Action extends PhetioObject {

    /**
     * @param {function} action - the function that is called when this Action occurs
     * @param {Object} [options]
     */
    constructor( action, options ) {
      options = _.extend( {

        // {Object[]} - see PARAMETER_KEYS for a list of legal keys, their types, and documentation
        parameters: EMPTY_ARRAY,

        // phet-io - see PhetioObject.js for doc
        tandem: Tandem.optional,

        // {function(new:ObjectIO) - override this to create a subtype of ActionIO as the phetioType instead of ActionIO.
        phetioOuterType: ActionIO,
        phetioState: false,
        phetioPlayback: PhetioObject.DEFAULT_OPTIONS.phetioPlayback,
        phetioEventMetadata: PhetioObject.DEFAULT_OPTIONS.phetioEventMetadata,
        phetioDocumentation: 'A function that executes.'
      }, options );

      assert && assert( options.phetioType === undefined,
        'Action sets its own phetioType. Instead provide parameter phetioTypes through `options.parameters`' );
      assert && assert( typeof action === 'function', 'action should be a function' );
      if ( options.tandem.supplied ) {
        assert && options.parameters.forEach( param => assert( param.phetioType, 'phetioType required for each parameter.' ) );

        // TODO: create a map so that we don't create this for each Emitter, https://github.com/phetsims/axon/issues/257
        // TODO: we should make sure to remove these items to prevent too large of a footprint, https://github.com/phetsims/axon/issues/257
        // TODO: the name in built mode will be minified, is that still ok? https://github.com/phetsims/axon/issues/257
        const uniqueKey = options.phetioOuterType.name + options.parameters.map( param => param.phetioType.typeName ).join( ',' );

        options.phetioType = TYPE_IO_MAP[ uniqueKey ] ? TYPE_IO_MAP[ uniqueKey ] : options.phetioOuterType( options.parameters.map( param => param.phetioType ) );
      }
      assert && Action.validateParameters( options.tandem.supplied, options.parameters );

      // phetioPlayback events need to know the order the arguments occur in order to call EmitterIO.emit()
      // Indicate whether the event is for playback, but leave this "sparse"--only indicate when this happens to be true
      if ( options.phetioPlayback ) {
        options.phetioEventMetadata = options.phetioEventMetadata || {};

        assert && assert( !options.phetioEventMetadata.hasOwnProperty( 'dataKeys' ),
          'dataKeys should be supplied by Action, not elsewhere' );

        options.phetioEventMetadata.dataKeys = options.parameters.map( parmeter => parmeter.name );
      }
      // We only need phetioDocumentation for instrumented instances
      if ( options.tandem.supplied ) {
        options.phetioDocumentation = Action.getPhetioDocumentation( options.phetioDocumentation, options.parameters );
      }
      super( options );

      // @public (only for testing) - Note: one test indicates stripping this out via assert && in builds may save around 300kb heap
      this.parameters = options.parameters;

      // @private {function}
      this._action = action;
    }

    /**
     * @param {boolean} isPhetioInstrumented
     * @param {object} parameters
     */
    static validateParameters( isPhetioInstrumented, parameters ) {

      // validate the parameters object
      validate( parameters, { valueType: Array } );

      for ( let i = 0; i < parameters.length; i++ ) {
        const validator = parameters[ i ];
        assert && assert( validator.validateOptionsOnValidateValue !== true,
          'Action sets its own validateOptionsOnValidateValue for each argument type'
        );

        assert && assert( Object.getPrototypeOf( validator ) === Object.prototype,
          'Extra prototype on validator object is a code smell' );

        var keys = Object.keys( validator );
        for ( let i = 0; i < keys.length; i++ ) {
          const key = keys[ i ];
          assert && assert( PARAMETER_KEYS.indexOf( key ) >= 0, 'unrecognized parameter key: ' + key );
        }

        assert && isPhetioInstrumented && assert( validator.phetioType, 'instrumented Emitters must include phetioType for each parameter' );

        assert && !validator.phetioType && assert( Object.keys( _.pick( validator, ValidatorDef.VALIDATOR_KEYS ) ).length > 0, 'if phetioType is not provided, validator must be specified' );

        let containsValidatorKeys = false;
        for ( var prop in validator ) {
          if ( ValidatorDef.VALIDATOR_KEYS.includes( prop ) ) {
            containsValidatorKeys = true;
            break;
          }
        }

        // TODO: this doesn't work now because of VoidIO workaround, do we want it to? https://github.com/phetsims/axon/issues/257
        // assert && assert( !!validator.phetioType !== !!validator.valueType, 'Specify phetioType OR valueType, not both' );

        // TODO: is this taking up too much memory? Does this create too much garbage? https://github.com/phetsims/axon/issues/257
        parameters[ i ] = _.extend( {
          validateOptionsOnValidateValue: false,
          containsValidatorKeys: containsValidatorKeys
        }, validator );

        // validate the options passed in to validate each Action argument
        // TODO: surely this is not the most efficient way to do this, https://github.com/phetsims/axon/issues/257
        containsValidatorKeys && ValidatorDef.validateValidator( validator );
      }

      // Changing after construction indicates a logic error.
      Object.freeze( parameters );
    }

    /**
     * Gets the data that will be emitted to the PhET-iO data stream, for an instrumented simulation.
     * @returns {Object} - the data, keys dependent on parameter metadata
     * @private
     */
    getPhetioData() {

      // null if there are no arguments. dataStream.js omits null values for data
      let data = null;
      if ( this.parameters.length > 0 ) {

        // Enumerate named argsObject for the data stream.
        data = {};
        for ( let i = 0; i < this.parameters.length; i++ ) {
          const element = this.parameters[ i ];
          data[ element.name ] = element.phetioType.toStateObject( arguments[ i ] );
        }
      }
      return data;
    }

    /**
     * Get the phetioDocumentation compiled from all the parameters
     * @param {boolean} currentPhetioDocumentation
     * @param {Object} parameters - see options.parameters
     * @private
     * @returns {string}
     */
    static getPhetioDocumentation( currentPhetioDocumentation, parameters ) {
      const paramToDocString = param => {
        var docText = param.phetioDocumentation ? '. ' + param.phetioDocumentation : '';

        // TODO: are we gauranteed to have the name? We aren't asserting it right now, https://github.com/phetsims/axon/issues/257
        return '
' + param.name + ': ' + param.phetioType.typeName + docText + '
';
      };

      return currentPhetioDocumentation + ( parameters.length === 0 ? ' No arguments.' : ' The arguments are:
' +
             '
' + parameters.map( paramToDocString ).join( '\n' ) + '
' );
    }

    /**
     * Invokes the action.
     * @params - expected parameters are based on options.parameters, see constructor
     * @public
     */
    execute() {
      if ( assert ) {
        assert( arguments.length === this.parameters.length,
          `Emitted unexpected number of args. Expected: ${this.parameters.length} and received ${arguments.length}`
        );
        for ( let i = 0; i < this.parameters.length; i++ ) {
          const parameter = this.parameters[ i ];
          if ( parameter.containsValidatorKeys ) {
            validate( arguments[ i ], parameter );
          }

          // valueType overrides the phetioType validator so we don't use that one if there is a valueType
          if ( parameter.phetioType && !this.parameters.valueType ) {
            validate( arguments[ i ], parameter.phetioType.validator );
          }
        }
      }

      // handle phet-io data stream for the emitted event
      this.isPhetioInstrumented() && this.phetioStartEvent( 'emitted', this.getPhetioData.apply( this, arguments ) );

      this._action.apply( null, arguments );

      this.isPhetioInstrumented() && this.phetioEndEvent();
    }
  }

  return axon.register( 'Action', Action );
} );<commit_msg>create phetioPrivate to hide action parameters from phet-io, https://github.com/phetsims/axon/issues/257<commit_after>// Copyright 2019, University of Colorado Boulder

/**
 * An action that can be executed and sent to the PhET-iO data stream, and optionally recorded for playback. This type
 * will also validate the argument types passed to the action function.
 *
 * @author Sam Reid (PhET Interactive Simulations)
 * @author Michael Kauzmann (PhET Interactive Simulations)
 */
define( require => {
  'use strict';

  // modules
  const ActionIO = require( 'AXON/ActionIO' );
  const assertMutuallyExclusiveOptions = require( 'PHET_CORE/assertMutuallyExclusiveOptions' );
  const axon = require( 'AXON/axon' );
  const PhetioObject = require( 'TANDEM/PhetioObject' );
  const Tandem = require( 'TANDEM/Tandem' );
  const validate = require( 'AXON/validate' );
  const ValidatorDef = require( 'AXON/ValidatorDef' );

  // constants
  // Simulations have thousands of Emitters, so we re-use objects where possible.
  const EMPTY_ARRAY = [];
  assert && Object.freeze( EMPTY_ARRAY );

  // TODO: Map that holds all the ActionIO types created so they can be reused when possible, https://github.com/phetsims/axon/issues/257
  const TYPE_IO_MAP = {};

  // allowed keys to options.parameters
  const PARAMETER_KEYS = [
    'name', // {string}
    'phetioType', // {function(new:ObjectIO)}
    'phetioDocumentation', // {string}

    // {boolean} - specify this to keep the parameter private to the phet-io api. To support emitting and executing over
    // the phet-io api, phetioPrivate parameters must not ever be before a public one. For example
    // `emit1( public1, private1, public2)` is not allowed. Instead it must be ordered like `emit( public1, public2, private1 )`
    'phetioPrivate'

  ].concat( ValidatorDef.VALIDATOR_KEYS );

  // helper closures
  const paramToPhetioType = param => param.phetioType;
  const paramToTypeName = param => param.phetioType.typeName;

  class Action extends PhetioObject {

    /**
     * @param {function} action - the function that is called when this Action occurs
     * @param {Object} [options]
     */
    constructor( action, options ) {
      options = _.extend( {

        // {Object[]} - see PARAMETER_KEYS for a list of legal keys, their types, and documentation
        parameters: EMPTY_ARRAY,

        // phet-io - see PhetioObject.js for doc
        tandem: Tandem.optional,

        // {function(new:ObjectIO) - override this to create a subtype of ActionIO as the phetioType instead of ActionIO.
        phetioOuterType: ActionIO,
        phetioState: false,
        phetioPlayback: PhetioObject.DEFAULT_OPTIONS.phetioPlayback,
        phetioEventMetadata: PhetioObject.DEFAULT_OPTIONS.phetioEventMetadata,
        phetioDocumentation: 'A function that executes.'
      }, options );

      // TODO: do we need the supplied check here? https://github.com/phetsims/axon/issues/257
      assert && Action.validateParameters( options.tandem.supplied, options.parameters );
      assert && assert( typeof action === 'function', 'action should be a function' );
      assert && assert( options.phetioType === undefined,
        'Action sets its own phetioType. Instead provide parameter phetioTypes through `options.parameters`' );

      // {Object[]} - list of parameters, see options.parameters. Filter out phetioPrivate parameters, all `phetioPrivate`
      // parameters will not have a `phetioType`, see `validateParameters`.
      const phetioPublicParameters = options.parameters.filter( paramToPhetioType );

      // TODO: do we need the supplied check here? https://github.com/phetsims/axon/issues/257
      if ( options.tandem.supplied ) {

        // TODO: create a map so that we don't create this for each Emitter, https://github.com/phetsims/axon/issues/257
        // TODO: we should make sure to remove these items to prevent too large of a footprint, https://github.com/phetsims/axon/issues/257
        // TODO: the name in built mode will be minified, is that still ok? https://github.com/phetsims/axon/issues/257
        const uniqueKey = options.phetioOuterType.name + '.' + phetioPublicParameters.map( paramToTypeName ).join( ',' );
        options.phetioType = TYPE_IO_MAP[ uniqueKey ] ? TYPE_IO_MAP[ uniqueKey ] : options.phetioOuterType( phetioPublicParameters.map( paramToPhetioType ) );
      }

      // phetioPlayback events need to know the order the arguments occur in order to call EmitterIO.emit()
      // Indicate whether the event is for playback, but leave this "sparse"--only indicate when this happens to be true
      if ( options.phetioPlayback ) {
        options.phetioEventMetadata = options.phetioEventMetadata || {};

        assert && assert( !options.phetioEventMetadata.hasOwnProperty( 'dataKeys' ),
          'dataKeys should be supplied by Action, not elsewhere' );

        options.phetioEventMetadata.dataKeys = options.parameters.map( parmeter => parmeter.name );
      }
      // We only need phetioDocumentation for instrumented instances
      if ( options.tandem.supplied ) {
        options.phetioDocumentation = Action.getPhetioDocumentation( options.phetioDocumentation, phetioPublicParameters );
      }
      super( options );

      // @public (only for testing) - Note: one test indicates stripping this out via assert && in builds may save around 300kb heap
      this.parameters = options.parameters;

      // @private {function}
      this._action = action;
    }

    /**
     * @param {boolean} isPhetioInstrumented
     * @param {object} parameters
     */
    static validateParameters( isPhetioInstrumented, parameters ) {

      // validate the parameters object
      validate( parameters, { valueType: Array } );

      // Action only supports phetioPrivate parameters at the end of the emit call, so once we hit the first phetioPrivate
      // parameter, then assert that the rest of them afterwards are as well.
      let reachedPhetioPrivate = false;
      for ( let i = 0; i < parameters.length; i++ ) {
        const parameter = parameters[ i ]; // metadata about a single parameter

        assert && assert( parameter.validateOptionsOnValidateValue !== true,
          'Action sets its own validateOptionsOnValidateValue for each argument type'
        );
        assert && assert( Object.getPrototypeOf( parameter ) === Object.prototype,
          'Extra prototype on parameter object is a code smell' );

        reachedPhetioPrivate = reachedPhetioPrivate || parameter.phetioPrivate;
        assert && reachedPhetioPrivate && assert( parameter.phetioPrivate,
          'after first phetioPrivate parameter, all subsequenct parameters must be phetioPrivate' );

        var keys = Object.keys( parameter );
        for ( let i = 0; i < keys.length; i++ ) {
          const key = keys[ i ];
          assert && assert( PARAMETER_KEYS.includes( key ), 'unrecognized parameter key: ' + key );
        }

        assert && isPhetioInstrumented && assert( parameter.phetioType || parameter.phetioPrivate,
          'instrumented Emitters must include phetioType for each parameter or be marked as `phetioPrivate`.' );

        assert && assertMutuallyExclusiveOptions( parameter, [ 'phetioPrivate' ], [
          'name', 'phetioType', 'phetioDocumentation'
        ] );

        // TODO: get rid of phetioTYpe check once ValidatorDef supports phetioType
        assert && !parameter.phetioType && assert( Object.keys( _.pick( parameter, ValidatorDef.VALIDATOR_KEYS ) ).length > 0,
          'if phetioType is not provided, parameter must be specified' );

        let containsValidatorKeys = false;
        for ( var prop in parameter ) {
          if ( ValidatorDef.VALIDATOR_KEYS.includes( prop ) ) {
            containsValidatorKeys = true;
            break;
          }
        }

        // TODO: is this taking up too much memory? Does this create too much garbage? https://github.com/phetsims/axon/issues/257
        parameters[ i ] = _.extend( {
          validateOptionsOnValidateValue: false,
          containsValidatorKeys: containsValidatorKeys
        }, parameter );

        // validate the options passed in to validate each Action argument
        // TODO: surely this is not the most efficient way to do this, https://github.com/phetsims/axon/issues/257
        containsValidatorKeys && ValidatorDef.validateValidator( parameter );
      }

      // Changing after construction indicates a logic error.
      Object.freeze( parameters );
    }

    /**
     * Gets the data that will be emitted to the PhET-iO data stream, for an instrumented simulation.
     * @returns {Object} - the data, keys dependent on parameter metadata
     * @private
     */
    getPhetioData() {

      // null if there are no arguments. dataStream.js omits null values for data
      let data = null;
      if ( this.parameters.length > 0 ) {

        // Enumerate named argsObject for the data stream.
        data = {};
        for ( let i = 0; i < this.parameters.length; i++ ) {
          const element = this.parameters[ i ];
          if ( !element.phetioPrivate ) {
            data[ element.name ] = element.phetioType.toStateObject( arguments[ i ] );
          }
        }
      }
      return data;
    }

    /**
     * Get the phetioDocumentation compiled from all the parameters
     * @param {boolean} currentPhetioDocumentation
     * @param {Object} parameters - see options.parameters
     * @private
     * @returns {string}
     */
    static getPhetioDocumentation( currentPhetioDocumentation, parameters ) {
      const paramToDocString = param => {
        var docText = param.phetioDocumentation ? '. ' + param.phetioDocumentation : '';

        // TODO: are we gauranteed to have the name? We aren't asserting it right now, https://github.com/phetsims/axon/issues/257
        return '
' + param.name + ': ' + param.phetioType.typeName + docText + '
';
      };

      return currentPhetioDocumentation + ( parameters.length === 0 ? ' No arguments.' : ' The arguments are:
' +
             '
' + parameters.map( paramToDocString ).join( '\n' ) + '
' );
    }

    /**
     * Invokes the action.
     * @params - expected parameters are based on options.parameters, see constructor
     * @public
     */
    execute() {
      if ( assert ) {
        assert( arguments.length === this.parameters.length,
          `Emitted unexpected number of args. Expected: ${this.parameters.length} and received ${arguments.length}`
        );
        for ( let i = 0; i < this.parameters.length; i++ ) {
          const parameter = this.parameters[ i ];
          if ( parameter.containsValidatorKeys ) {
            validate( arguments[ i ], parameter );
          }

          // valueType overrides the phetioType validator so we don't use that one if there is a valueType
          if ( parameter.phetioType && !this.parameters.valueType ) {
            validate( arguments[ i ], parameter.phetioType.validator );
          }
        }
      }

      // handle phet-io data stream for the emitted event
      this.isPhetioInstrumented() && this.phetioStartEvent( 'emitted', this.getPhetioData.apply( this, arguments ) );

      this._action.apply( null, arguments );

      this.isPhetioInstrumented() && this.phetioEndEvent();
    }
  }

  return axon.register( 'Action', Action );
} );<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react';
import SVGInjector from 'svg-injector';

export default class ReactSVG extends Component {

  static defaultProps = {
    evalScripts: 'never',
    callback: () => {}
  }

  static propTypes = {
    path: PropTypes.string.isRequired,
    className: PropTypes.string,
    evalScripts: PropTypes.oneOf(['always', 'once', 'never']),
    fallbackPath: PropTypes.string,
    callback: PropTypes.func
  }

  render() {

    const {
      className,
      path,
      fallbackPath
    } = this.props;

    return (
      <img
        className={className}
        data-src={path}
        data-fallback={fallbackPath}
        ref={(img) => this._img = img}
      />
    );

  }

  componentDidMount() {

    const {
      evalScripts,
      callback: each
    } = this.props;

    SVGInjector(this._img, {
      evalScripts,
      each
    });

  }

}
<commit_msg>Update SVG when component is updated<commit_after>import React, { Component, PropTypes } from 'react';
import SVGInjector from 'svg-injector';

export default class ReactSVG extends Component {

  static defaultProps = {
    evalScripts: 'never',
    callback: () => {}
  }

  static propTypes = {
    path: PropTypes.string.isRequired,
    className: PropTypes.string,
    evalScripts: PropTypes.oneOf(['always', 'once', 'never']),
    fallbackPath: PropTypes.string,
    callback: PropTypes.func
  }

  render() {

    const {
      className,
      path,
      fallbackPath
    } = this.props;

    return (
      <img
        className={className}
        data-src={path}
        data-fallback={fallbackPath}
        ref={(img) => this._img = img}
      />
    );

  }

  componentDidMount() {
    this.updateSVG();
  }

  componentDidUpdate() {
    this.updateSVG();
  }

  updateSVG() {

    const {
      evalScripts,
      callback: each
    } = this.props;

    SVGInjector(this._img, {
      evalScripts,
      each
    });

  }

}
<|endoftext|>