<commit_before>/**
 *  Copyright (c) 2014, StudioLabs, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant
 *  of patent rights can be found in the PATENTS file in the same directory.
 */

/*global Connection:false */

(function() {
	'use strict';

	/**
	   * Export to Node for testing and to global for production.
	   */

	if (typeof module === 'object' && typeof exports === 'object') {
		module.exports = Session;
	} else {
		this.Session = Session;
	}

	/**
	   * Manages a user sessios.
	   *
	   * @param {string} host
	   * @param {number} port
	   * @param {function} status
	   * @param {function} logger
	   * @class Session
	   * @public
	   */

	function Session(host, port, status, createLogger) {
		this.host = host;
		this.port = port;
		this.status = status;
		this.createLogger = createLogger;
		this.logger = createLogger('session');
		this.devResources = [];
		this.openedSources = {};
		this.url = null;
		this.conn = null;
		this.forceReloading = false;
		this.messageHandler = this.messageHandler.bind(this);
		this.started = this.started.bind(this);
	}

	/**
	   * Registers the resources, connects to server and listens to events.
	   *
	   * @public
	   */

	Session.prototype.start = function() {
		this.logger.log('Starting DevtoolsLive for host', this.host);
		this.getLocation(this.setLocation);
	};

	/**
	   * Similar to restart but does only what's needed to get DevtoolsLive started.
	   *
	   * @public
	   */

	Session.prototype.restart = function() {
		this.logger.log('Restarting');
		if (this.conn.connected()) {
			// No need to reconnect. We just refetch the resources.
			this.getResources(this.started.bind(this));
		} else {
			this.start();
		}
	};

	/**
	   * Force the reloading of the page
	   *
	   * @public
	   */

	Session.prototype.setForceReloading = function(value) {
		this.forceReloading = (value == 'enable') ? true : false;
	};

	/**
	   * Registers a resource.
	   *
	   * @param {function} res
	   * @private
	   */

	Session.prototype.registerResource = function(res) {
		// exclude ressource that are data
		//
		if (res.url.substr(0, 4) == 'http') {
			var url = res.url.split('?')[0].replace(this.url, '');
			if (url !== '') {
				this.devResources[url] = res;
				if(this.loader !== undefined){

					this.loader.urls = this.loader.urls.filter(function(e){
						return (url.indexOf(e) == -1)
					});

					if(this.loader.urls.length == 0){
						var toSync = this.loader.toSync;
						delete this.loader;
					}

					if(toSync !== undefined){
						this.conn.sendMessage({
							action: 'sync',
							src: toSync
						});
					}
				}
			}
		}

	};

	/**
	   * save the url location then start getting resources.
	   *
	   * @param {function} callback
	   * @private
	   */

	Session.prototype.setLocation = function(url) {
		if (url) {
			this.url = url;
			this.getResources(this.connect.bind(this, this.started));
		}else {
			this.logger.log('erorr on location');
		}
	};

	/**
	   * Get the url location of the inspected window.
	   *
	   * @param {function} callback
	   * @private
	   */

	Session.prototype.getLocation = function(callback) {
		chrome.devtools.inspectedWindow['eval'](
		'location.origin+location.pathname',
		callback.bind(this)
		);
	};

	/**
	   * Registers the resources and listens to onResourceAdded events.
	   *
	   * @param {function} callback
	   * @private
	   */

	Session.prototype.getResources = function(callback) {

		chrome.devtools.inspectedWindow.getResources(function(resources) {

			resources.forEach(function(res) {
				this.registerResource(res);
			}.bind(this));

			// After we register the current resources, we listen to the
			// onResourceAdded event to push on more resources lazily fetched
			// to our array.
			chrome.devtools.inspectedWindow.onResourceAdded.addListener(
				function(res) { this.registerResource(res); }.bind(this)
			);

			if(callback !== undefined){
				this.console(' ', 'Ready !');

				callback();
			}

		}.bind(this));
	};

	/**
	   * Connect to server.
	   *
	   * @param {function} callback
	   * @private
	   */

	Session.prototype.connect = function(callback) {

		callback = once(callback);
		var self = this;
		this.conn = new Connection(this.host, this.port, this.createLogger)
		.onmessage(this.messageHandler)
      .onerror(function() {
	self.status('error');
      })
      .onopen(function() {
	self.status('connected');
	callback();
      })
      .onretry(function(delay) {
	self.status('retry', delay);
      })
      .onconnecting(function() {
	self.status('connecting');
      })
      .connect();

	};

	/**
	   * Does whatever needs to be done after the session is started. Currenlty
	   * just listening to page refresh events.
	   *
	   * @param {function} callback
	   */

	Session.prototype.started = function() {
		this.logger.log('Started');
		this.status('started');

		chrome.devtools.network.onNavigated.addListener(this.restart);
		chrome.devtools.inspectedWindow.onResourceContentCommitted.addListener(this.resourceUpdatedHandler.bind(this) );
	};

	/**
	   * Handle Resource Updated.
	   *
	   * @param {object} updatedResource
	   * @param {string} content
	   * @private
	   */
	Session.prototype.resourceUpdatedHandler = function(updatedResource, content) {

	 	if(updatedResource.registered == undefined){

			var src = updatedResource.url.split('?')[0].replace(this.url, '');

			if (src !== '' && this.devResources[src] !== undefined) {
				var resource = this.devResources[src];

				if (resource.resourceName !== undefined) {

					this.triggerUpdateEvent(resource.resourceName);

				}else if (resource.sync !== undefined) {
						var sync = resource.sync;
						var record = {
							action: 'sync',
							src: sync
						};
						this.conn.sendMessage(record);
						delete resource.sync;
				} else{
					this.conn.sendMessage({
						action: 'update',
						src: src,
						content: content
					});
				}

			}

			updatedResource.registered = true;

		}

	};

	/***
	* Update browser resouce.
    * @param { object }	resource
    */

	 Session.prototype.updateResource = function(resource, update) {
	 	console.log('updateResource',resource, update);

		var setContent = function() {
			resource.setContent(update.content, true, function(status) {
				if (status.code != 'OK') {
					this.logger.error(
					'devtoolsLive failed to update, this shouldn\'t happen please report it: ' +
					JSON.stringify(status)
					);
				}else {
					if (update.event !== undefined) {
						if (update.resourceName !== undefined && !this.openedSources[update.resourceName]) {
							chrome.devtools.panels.openResource(this.devResources[update.resourceName].url, null, function() {
								this.openedSources[update.resourceName] = true;
								this.triggerEvent(update.event, {});
							}.bind(this));
						}else{
							this.triggerEvent(update.event, {});
						}
					}
					if (resource.resourceName !== undefined) {
						delete resource.resourceName;
					}

					if(resource.update !== undefined){
						this.messageHandler(resource.update);
						delete resource.update;
					}
				}
			}.bind(this));
		}.bind(this);

		if (!this.openedSources[update.url]) {
			chrome.devtools.panels.openResource(resource.url, null, function() {
				// doesn't always work right away
				this.openedSources[update.url] = true;
				setTimeout(setContent, 100);
			}.bind(this));
		} else {
			setContent();
		}

	};

	/***
	* Handler for messages from the server.
    * @param { object }	message
    */

	Session.prototype.messageHandler = function(message) {
		console.log('messageHandler', message);

		if (message.action == 'baseUrl' && this.conn && this.url) {
			return this.conn.sendMessage({
				action: 'baseUrl',
				url: this.url
			});
		}else if (this.forceReloading === true) {
			this.reload();
			return;
		}else if (message.action == 'urls') {
			if(message.next != undefined){
				if (this.devResources[message.next.url] !== undefined) {
					this.loader = this.devResources[message.next.url];
					this.loader.toSync = message.next.url;
					this.loader.update = message.next.update;
					if (message.data !== undefined) {
						this.loader.urls = message.data.urls;

					}
				}
			}

			if (message.data !== undefined) {
				this.addFiles(message.data.html);
			}

			return;
		}else if (message.action == 'error') {

			if (this.devResources[message.src] !== undefined) {
				var resource = this.devResources[message.src];
				this.updateResource(resource, message);
			}

			this.error(message.url, message);

		}else if (message.action == 'sync') {

			this.logger.log('sync', message.url);

			if (this.devResources[message.url] !== undefined) {
				var resource = this.devResources[message.url];
				resource.resourceName = message.url;
			}

		}else if (message.action == 'update') {

			this.logger.log('push', message.url);

			if (message.reload === true) {
				chrome.devtools.inspectedWindow.reload({ignoreCache: true});
				return;
			}

			if (message.url.indexOf('index.html') > 0) {
				message.url = message.url.replace('index.html', '');
			}

			if (this.devResources[message.url] !== undefined) {
				var resource = this.devResources[message.url];
			}

		}

		if (resource === undefined) {
			this.logger.error(
			'Resource with the following URL is not on the page:',
			message.url
			);
			return;
		}

		if (message.resourceName !== undefined) {
			resource.resourceName = message.resourceName;
		}

		if (message.sync !== undefined) {
			resource.sync = message.sync;
		}

		if (message.part !== undefined) {

			if (resource.part === undefined) {
				resource.part = [];
			}

			// store each part
			resource.part.push(message.part);

		}else {
			// concat all parts
			if (resource.part !== undefined) {
				resource.part.push(message.content);
				message.content = resource.part.join('');
				delete resource.part;
			}

			this.updateResource(resource,message);
		}

	};

	/**
	   * Destroys session.
	   *
	   * @public
	   */

	Session.prototype.destroy = function() {
		if (this.conn) this.conn.disconnect();
	};

	/**
	   * Utility to ensure's a function is called only once.
	   *
	   * @param {function} cb
	   * @private
	   */

	function once(cb) {
		var called = false;
		return function() {
			if (!called) {
				called = true;
				return cb.apply(this, arguments);
			}
		};
	}

	Session.prototype.error = function(title, data) {
		var dataB64 = window.btoa(unescape(encodeURIComponent(JSON.stringify(data))));
		var data = decodeURIComponent(escape(window.atob(dataB64)));
		var script = '(function() {' +
		'var data = ' + data + ' ;' +
		'console.warn(data.message);' +
		'})()';

		chrome.devtools.inspectedWindow.eval(script);
	}

	Session.prototype.addFiles = function(html) {

			var dataB64 = window.btoa(unescape(encodeURIComponent(JSON.stringify(html))));
			var data = decodeURIComponent(escape(window.atob(dataB64)));

			var script = '(function() {' +
			'var html = ' + data + ' ;' +
			'for (var i in html){'+
				'var data = html[i] ;' +
				'var el = document.createElement(data.tag);'+
				'for(var y in data.attributes){'+
				'	el.setAttribute(y,data.attributes[y]);'+
				'}'+
				'console.log("[Live][add]", el);' +
				'document.body.appendChild(el);' +
			'}'+
			'})()';

			chrome.devtools.inspectedWindow.eval(script);

	}

	Session.prototype.console = function(title, data) {
		var dataB64 = window.btoa(unescape(encodeURIComponent(JSON.stringify(data))));
		var data = decodeURIComponent(escape(window.atob(dataB64)));
		var script = '(function() {' +
		'var data = ' + data + ' ;' +
		'console.log("[Live] ' + title + '",data);' +
		'})()';

		chrome.devtools.inspectedWindow.eval(script);
	}

	Session.prototype.triggerEvent = function(event, data) {
		var dataB64 = window.btoa(unescape(encodeURIComponent(JSON.stringify(data))));
		var data = decodeURIComponent(escape(window.atob(dataB64)));

		var script = '(function() {' +
		'var event = new Event(\'' + event + '\');' +
		'event.data = ' + data + ' ;' +
		'window.dispatchEvent(event);' +
		'})()';

		chrome.devtools.inspectedWindow.eval(script);
	};

	Session.prototype.reload = function(url) {

		var script = '(function() {' +
		'window.addEventListener(\'devtools-live-reload\',function(){' +
		'console.log("[reload] reloading the page...");' +
		'}); ' +
		'})()';

		this.triggerEvent('devtools-live-reload', {});

		chrome.devtools.inspectedWindow.eval(script);

		chrome.devtools.inspectedWindow.reload({ignoreCache: true});

	};

	Session.prototype.addRessource = function(url, contentScript) {

		if (typeof url == 'string') {
			var script = '(function() {' +
			'console.log("[add] ' + this.url+url + ' has just been updated ["+ time +"] ");' +
			 contentScript+
			'})()';

			chrome.devtools.inspectedWindow.eval(script);
			chrome.devtools.panels.openResource(url, null, function() {});
		}

	};

	Session.prototype.triggerUpdateEvent = function(url) {

		if (typeof url == 'string') {
			var script = '(function() {' +
			'var time = new Date().getTime();' +
			'console.log("[update] ' + this.url+url + ' has just been updated ["+ time +"] ");' +
			'})()';

			chrome.devtools.inspectedWindow.eval(script);
			chrome.devtools.panels.openResource(this.url+url, null, function() {});
		}

	};

}).call(this);
<commit_msg>fix client<commit_after>/**
 *  Copyright (c) 2014, StudioLabs, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant
 *  of patent rights can be found in the PATENTS file in the same directory.
 */

/*global Connection:false */

(function() {
	'use strict';

	/**
	   * Export to Node for testing and to global for production.
	   */

	if (typeof module === 'object' && typeof exports === 'object') {
		module.exports = Session;
	} else {
		this.Session = Session;
	}

	/**
	   * Manages a user sessios.
	   *
	   * @param {string} host
	   * @param {number} port
	   * @param {function} status
	   * @param {function} logger
	   * @class Session
	   * @public
	   */

	function Session(host, port, status, createLogger) {
		this.host = host;
		this.port = port;
		this.status = status;
		this.createLogger = createLogger;
		this.logger = createLogger('session');
		this.devResources = [];
		this.openedSources = {};
		this.url = null;
		this.conn = null;
		this.forceReloading = false;
		this.messageHandler = this.messageHandler.bind(this);
		this.started = this.started.bind(this);
	}

	/**
	   * Registers the resources, connects to server and listens to events.
	   *
	   * @public
	   */

	Session.prototype.start = function() {
		this.logger.log('Starting DevtoolsLive for host', this.host);
		this.getLocation(this.setLocation);
	};

	/**
	   * Similar to restart but does only what's needed to get DevtoolsLive started.
	   *
	   * @public
	   */

	Session.prototype.restart = function() {
		this.logger.log('Restarting');
		if (this.conn.connected()) {
			// No need to reconnect. We just refetch the resources.
			this.getResources(this.started.bind(this));
		} else {
			this.start();
		}
	};

	/**
	   * Force the reloading of the page
	   *
	   * @public
	   */

	Session.prototype.setForceReloading = function(value) {
		this.forceReloading = (value == 'enable') ? true : false;
	};

	/**
	   * Registers a resource.
	   *
	   * @param {function} res
	   * @private
	   */

	Session.prototype.registerResource = function(res) {
		// exclude ressource that are data
		//
		if (res.url.substr(0, 4) == 'http') {
			var url = res.url.split('?')[0].replace(this.url, '');
			if (url !== '') {
				this.devResources[url] = res;
				if(this.loader !== undefined){

					this.loader.urls = this.loader.urls.filter(function(e){
						return (url.indexOf(e) == -1)
					});

					if(this.loader.urls.length == 0){
						var toSync = this.loader.toSync;
						delete this.loader;
					}

					if(toSync !== undefined){
						this.conn.sendMessage({
							action: 'sync',
							src: toSync
						});
					}
				}
			}
		}

	};

	/**
	   * save the url location then start getting resources.
	   *
	   * @param {function} callback
	   * @private
	   */

	Session.prototype.setLocation = function(url) {
		if (url) {
			this.url = url;
			this.getResources(this.connect.bind(this, this.started));
		}else {
			this.logger.log('erorr on location');
		}
	};

	/**
	   * Get the url location of the inspected window.
	   *
	   * @param {function} callback
	   * @private
	   */

	Session.prototype.getLocation = function(callback) {
		chrome.devtools.inspectedWindow['eval'](
		'location.origin+location.pathname',
		callback.bind(this)
		);
	};

	/**
	   * Registers the resources and listens to onResourceAdded events.
	   *
	   * @param {function} callback
	   * @private
	   */

	Session.prototype.getResources = function(callback) {

		chrome.devtools.inspectedWindow.getResources(function(resources) {

			resources.forEach(function(res) {
				this.registerResource(res);
			}.bind(this));

			// After we register the current resources, we listen to the
			// onResourceAdded event to push on more resources lazily fetched
			// to our array.
			chrome.devtools.inspectedWindow.onResourceAdded.addListener(
				function(res) { this.registerResource(res); }.bind(this)
			);

			if(callback !== undefined){
				this.console(' ', 'Ready !');

				callback();
			}

		}.bind(this));
	};

	/**
	   * Connect to server.
	   *
	   * @param {function} callback
	   * @private
	   */

	Session.prototype.connect = function(callback) {

		callback = once(callback);
		var self = this;
		this.conn = new Connection(this.host, this.port, this.createLogger)
		.onmessage(this.messageHandler)
      .onerror(function() {
	self.status('error');
      })
      .onopen(function() {
	self.status('connected');
	callback();
      })
      .onretry(function(delay) {
	self.status('retry', delay);
      })
      .onconnecting(function() {
	self.status('connecting');
      })
      .connect();

	};

	/**
	   * Does whatever needs to be done after the session is started. Currenlty
	   * just listening to page refresh events.
	   *
	   * @param {function} callback
	   */

	Session.prototype.started = function() {
		this.logger.log('Started');
		this.status('started');

		chrome.devtools.network.onNavigated.addListener(this.restart);
		chrome.devtools.inspectedWindow.onResourceContentCommitted.addListener(this.resourceUpdatedHandler.bind(this) );
	};

	/**
	   * Handle Resource Updated.
	   *
	   * @param {object} updatedResource
	   * @param {string} content
	   * @private
	   */
	Session.prototype.resourceUpdatedHandler = function(updatedResource, content) {

	 	if(updatedResource.registered == undefined){

			var src = updatedResource.url.split('?')[0].replace(this.url, '');

			if (src !== '' && this.devResources[src] !== undefined) {
				var resource = this.devResources[src];

				if (resource.resourceName !== undefined) {

					this.triggerUpdateEvent(resource.resourceName);

				}else if (resource.sync !== undefined) {
						var sync = resource.sync;
						var record = {
							action: 'sync',
							src: sync.replace(this.url, '')
						};
						this.conn.sendMessage(record);
						delete resource.sync;
				} else{
					this.conn.sendMessage({
						action: 'update',
						src: src,
						content: content
					});
				}

			}

			updatedResource.registered = true;

		}

	};

	/***
	* Update browser resouce.
    * @param { object }	resource
    */

	 Session.prototype.updateResource = function(resource, update) {
	 	console.log('updateResource',resource, update);

		var setContent = function() {
			resource.setContent(update.content, true, function(status) {
				if (status.code != 'OK') {
					this.logger.error(
					'devtoolsLive failed to update, this shouldn\'t happen please report it: ' +
					JSON.stringify(status)
					);
				}else {
					if (update.event !== undefined) {
						if (update.resourceName !== undefined && !this.openedSources[update.resourceName]) {
							chrome.devtools.panels.openResource(this.devResources[update.resourceName].url, null, function() {
								this.openedSources[update.resourceName] = true;
								this.triggerEvent(update.event, {});
							}.bind(this));
						}else{
							this.triggerEvent(update.event, {});
						}
					}
					if (resource.resourceName !== undefined) {
						delete resource.resourceName;
					}

					if(resource.update !== undefined){
						this.messageHandler(resource.update);
						delete resource.update;
					}
				}
			}.bind(this));
		}.bind(this);

		if (!this.openedSources[update.url]) {
			chrome.devtools.panels.openResource(resource.url, null, function() {
				// doesn't always work right away
				this.openedSources[update.url] = true;
				setTimeout(setContent, 100);
			}.bind(this));
		} else {
			setContent();
		}

	};

	/***
	* Handler for messages from the server.
    * @param { object }	message
    */

	Session.prototype.messageHandler = function(message) {
		console.log('messageHandler', message);

		if (message.action == 'baseUrl' && this.conn && this.url) {
			return this.conn.sendMessage({
				action: 'baseUrl',
				url: this.url
			});
		}else if (this.forceReloading === true) {
			this.reload();
			return;
		}else if (message.action == 'urls') {
			if(message.next != undefined){
				if (this.devResources[message.next.url] !== undefined) {
					this.loader = this.devResources[message.next.url];
					this.loader.toSync = message.next.url;
					this.loader.update = message.next.update;
					if (message.data !== undefined) {
						this.loader.urls = message.data.urls;

					}
				}
			}

			if (message.data !== undefined) {
				this.addFiles(message.data.html);
			}

			return;
		}else if (message.action == 'error') {

			if (this.devResources[message.src] !== undefined) {
				var resource = this.devResources[message.src];
				this.updateResource(resource, message);
			}

			this.error(message.url, message);

		}else if (message.action == 'sync') {

			this.logger.log('sync', message.url);

			if (this.devResources[message.url] !== undefined) {
				var resource = this.devResources[message.url];
				resource.resourceName = message.url;
			}

		}else if (message.action == 'update') {

			this.logger.log('push', message.url);

			if (message.reload === true) {
				chrome.devtools.inspectedWindow.reload({ignoreCache: true});
				return;
			}

			if (message.url.indexOf('index.html') > 0) {
				message.url = message.url.replace('index.html', '');
			}

			if (this.devResources[message.url] !== undefined) {
				var resource = this.devResources[message.url];
			}

		}

		if (resource === undefined) {
			this.logger.error(
			'Resource with the following URL is not on the page:',
			message.url
			);
			return;
		}

		if (message.resourceName !== undefined) {
			resource.resourceName = message.resourceName;
		}

		if (message.sync !== undefined) {
			resource.sync = message.sync;
		}

		if (message.part !== undefined) {

			if (resource.part === undefined) {
				resource.part = [];
			}

			// store each part
			resource.part.push(message.part);

		}else {
			// concat all parts
			if (resource.part !== undefined) {
				resource.part.push(message.content);
				message.content = resource.part.join('');
				delete resource.part;
			}

			this.updateResource(resource,message);
		}

	};

	/**
	   * Destroys session.
	   *
	   * @public
	   */

	Session.prototype.destroy = function() {
		if (this.conn) this.conn.disconnect();
	};

	/**
	   * Utility to ensure's a function is called only once.
	   *
	   * @param {function} cb
	   * @private
	   */

	function once(cb) {
		var called = false;
		return function() {
			if (!called) {
				called = true;
				return cb.apply(this, arguments);
			}
		};
	}

	Session.prototype.error = function(title, data) {
		var dataB64 = window.btoa(unescape(encodeURIComponent(JSON.stringify(data))));
		var data = decodeURIComponent(escape(window.atob(dataB64)));
		var script = '(function() {' +
		'var data = ' + data + ' ;' +
		'console.warn(data.message);' +
		'})()';

		chrome.devtools.inspectedWindow.eval(script);
	}

	Session.prototype.addFiles = function(html) {

			var dataB64 = window.btoa(unescape(encodeURIComponent(JSON.stringify(html))));
			var data = decodeURIComponent(escape(window.atob(dataB64)));

			var script = '(function() {' +
			'var html = ' + data + ' ;' +
			'for (var i in html){'+
				'var data = html[i] ;' +
				'var el = document.createElement(data.tag);'+
				'for(var y in data.attributes){'+
				'	el.setAttribute(y,data.attributes[y]);'+
				'}'+
				'console.log("[Live][add]", el);' +
				'document.body.appendChild(el);' +
			'}'+
			'})()';

			chrome.devtools.inspectedWindow.eval(script);

	}

	Session.prototype.console = function(title, data) {
		var dataB64 = window.btoa(unescape(encodeURIComponent(JSON.stringify(data))));
		var data = decodeURIComponent(escape(window.atob(dataB64)));
		var script = '(function() {' +
		'var data = ' + data + ' ;' +
		'console.log("[Live] ' + title + '",data);' +
		'})()';

		chrome.devtools.inspectedWindow.eval(script);
	}

	Session.prototype.triggerEvent = function(event, data) {
		var dataB64 = window.btoa(unescape(encodeURIComponent(JSON.stringify(data))));
		var data = decodeURIComponent(escape(window.atob(dataB64)));

		var script = '(function() {' +
		'var event = new Event(\'' + event + '\');' +
		'event.data = ' + data + ' ;' +
		'window.dispatchEvent(event);' +
		'})()';

		chrome.devtools.inspectedWindow.eval(script);
	};

	Session.prototype.reload = function(url) {

		var script = '(function() {' +
		'window.addEventListener(\'devtools-live-reload\',function(){' +
		'console.log("[reload] reloading the page...");' +
		'}); ' +
		'})()';

		this.triggerEvent('devtools-live-reload', {});

		chrome.devtools.inspectedWindow.eval(script);

		chrome.devtools.inspectedWindow.reload({ignoreCache: true});

	};

	Session.prototype.addRessource = function(url, contentScript) {

		if (typeof url == 'string') {
			var script = '(function() {' +
			'console.log("[add] ' + this.url+url + ' has just been updated ["+ time +"] ");' +
			 contentScript+
			'})()';

			chrome.devtools.inspectedWindow.eval(script);
			chrome.devtools.panels.openResource(url, null, function() {});
		}

	};

	Session.prototype.triggerUpdateEvent = function(url) {

		if (typeof url == 'string') {
			var script = '(function() {' +
			'var time = new Date().getTime();' +
			'console.log("[update] ' + this.url+url + ' has just been updated ["+ time +"] ");' +
			'})()';

			chrome.devtools.inspectedWindow.eval(script);
			chrome.devtools.panels.openResource(this.url+url, null, function() {});
		}

	};

}).call(this);
<|endoftext|>


<commit_before>'use strict';

/**
 * Module dependencies.
 */

const { inspect } = require('util');
const { STATUS_CODES } = require('http');
const { Server } = require('https');
const { deepStrictEqual } = require('assert');
const { Request } = require('superagent');

class Test extends Request {
  /**
   * Initialize a new `Test` with the given `app`,
   * request `method` and `path`.
   *
   * @param {Server} app
   * @param {String} method
   * @param {String} path
   * @api public
   */
  constructor (app, method, path) {
    super(method.toUpperCase(), path);

    this.redirects(0);
    this.buffer();
    this.app = app;
    this._asserts = [];
    this.url = typeof app === 'string'
      ? app + path
      : this.serverAddress(app, path);
  }

  /**
   * Returns a URL, extracted from a server.
   *
   * @param {Server} app
   * @param {String} path
   * @returns {String} URL address
   * @api private
   */
  serverAddress(app, path) {
    const addr = app.address();

    if (!addr) this._server = app.listen(0);
    const port = app.address().port;
    const protocol = app instanceof Server ? 'https' : 'http';
    return protocol + '://127.0.0.1:' + port + path;
  }

  /**
   * Expectations:
   *
   *   .expect(200)
   *   .expect(200, fn)
   *   .expect(200, body)
   *   .expect('Some body')
   *   .expect('Some body', fn)
   *   .expect(['json array body', { key: 'val' }])
   *   .expect('Content-Type', 'application/json')
   *   .expect('Content-Type', 'application/json', fn)
   *   .expect(fn)
   *   .expect([200, 404])
   *
   * @return {Test}
   * @api public
   */
  expect(a, b, c) {
    // callback
    if (typeof a === 'function') {
      this._asserts.push(wrapAssertFn(a));
      return this;
    }
    if (typeof b === 'function') this.end(b);
    if (typeof c === 'function') this.end(c);

    // status
    if (typeof a === 'number') {
      this._asserts.push(wrapAssertFn(this._assertStatus.bind(this, a)));
      // body
      if (typeof b !== 'function' && arguments.length > 1) {
        this._asserts.push(wrapAssertFn(this._assertBody.bind(this, b)));
      }
      return this;
    }

    // multiple statuses
    if (Array.isArray(a) && a.length > 0 && a.every(val => typeof val === 'number')) {
      this._asserts.push(wrapAssertFn(this._assertStatusArray.bind(this, a)));
      return this;
    }

    // header field
    if (typeof b === 'string' || typeof b === 'number' || b instanceof RegExp) {
      this._asserts.push(wrapAssertFn(this._assertHeader.bind(this, { name: '' + a, value: b })));
      return this;
    }

    // body
    this._asserts.push(wrapAssertFn(this._assertBody.bind(this, a)));

    return this;
  }

  /**
   * Defer invoking superagent's `.end()` until
   * the server is listening.
   *
   * @param {Function} fn
   * @api public
   */
  end(fn) {
    const self = this;
    const server = this._server;
    const end = Request.prototype.end;

    end.call(this, function (err, res) {
      if (server && server._handle) return server.close(localAssert);

      localAssert();

      function localAssert() {
        self.assert(err, res, fn);
      }
    });

    return this;
  }

  /**
   * Perform assertions and invoke `fn(err, res)`.
   *
   * @param {?Error} resError
   * @param {Response} res
   * @param {Function} fn
   * @api private
   */
  assert(resError, res, fn) {
    let errorObj;

    // check for unexpected network errors or server not running/reachable errors
    // when there is no response and superagent sends back a System Error
    // do not check further for other asserts, if any, in such case
    // https://nodejs.org/api/errors.html#errors_common_system_errors
    const sysErrors = {
      ECONNREFUSED: 'Connection refused',
      ECONNRESET: 'Connection reset by peer',
      EPIPE: 'Broken pipe',
      ETIMEDOUT: 'Operation timed out'
    };

    if (!res && resError) {
      if (resError instanceof Error && resError.syscall === 'connect'
        && Object.getOwnPropertyNames(sysErrors).indexOf(resError.code) >= 0) {
        errorObj = new Error(resError.code + ': ' + sysErrors[resError.code]);
      } else {
        errorObj = resError;
      }
    }

    // asserts
    for (let i = 0; i < this._asserts.length && !errorObj; i += 1) {
      errorObj = this._assertFunction(this._asserts[i], res);
    }

    // set unexpected superagent error if no other error has occurred.
    if (!errorObj && resError instanceof Error && (!res || resError.status !== res.status)) {
      errorObj = resError;
    }

    fn.call(this, errorObj || null, res);
  }

  /**
   * Perform assertions on a response body and return an Error upon failure.
   *
   * @param {Mixed} body
   * @param {Response} res
   * @return {?Error}
   * @api private
   */// eslint-disable-next-line class-methods-use-this
  _assertBody(body, res) {
    const isRegexp = body instanceof RegExp;

    // parsed
    if (typeof body === 'object' && !isRegexp) {
      try {
        deepStrictEqual(body, res.body);
      } catch (err) {
        const a = inspect(body);
        const b = inspect(res.body);
        return error('expected ' + a + ' response body, got ' + b, body, res.body);
      }
    } else if (body !== res.text) {
      // string
      const a = inspect(body);
      const b = inspect(res.text);

      // regexp
      if (isRegexp) {
        if (!body.test(res.text)) {
          return error('expected body ' + b + ' to match ' + body, body, res.body);
        }
      } else {
        return error('expected ' + a + ' response body, got ' + b, body, res.body);
      }
    }
  }

  /**
   * Perform assertions on a response header and return an Error upon failure.
   *
   * @param {Object} header
   * @param {Response} res
   * @return {?Error}
   * @api private
   */// eslint-disable-next-line class-methods-use-this
  _assertHeader(header, res) {
    const field = header.name;
    const actual = res.header[field.toLowerCase()];
    const fieldExpected = header.value;

    if (typeof actual === 'undefined') return new Error('expected "' + field + '" header field');
    // This check handles header values that may be a String or single element Array
    if ((Array.isArray(actual) && actual.toString() === fieldExpected)
      || fieldExpected === actual) {
      return;
    }
    if (fieldExpected instanceof RegExp) {
      if (!fieldExpected.test(actual)) {
        return new Error('expected "' + field + '" matching '
          + fieldExpected + ', got "' + actual + '"');
      }
    } else {
      return new Error('expected "' + field + '" of "' + fieldExpected + '", got "' + actual + '"');
    }
  }

  /**
   * Perform assertions on the response status and return an Error upon failure.
   *
   * @param {Number} status
   * @param {Response} res
   * @return {?Error}
   * @api private
   */// eslint-disable-next-line class-methods-use-this
  _assertStatus(status, res) {
    if (res.status !== status) {
      const a = STATUS_CODES[status];
      const b = STATUS_CODES[res.status];
      return new Error('expected ' + status + ' "' + a + '", got ' + res.status + ' "' + b + '"');
    }
  }

  /**
   * Perform assertions on the response status and return an Error upon failure.
   *
   * @param {Array<Number>} statusArray
   * @param {Response} res
   * @return {?Error}
   * @api private
   */// eslint-disable-next-line class-methods-use-this
  _assertStatusArray(statusArray, res) {
    if (!statusArray.includes(res.status)) {
      const b = STATUS_CODES[res.status];
      const expectedList = statusArray.join(', ');
      return new Error(
        'expected one of "' + expectedList + '", got ' + res.status + ' "' + b + '"'
      );
    }
  }

  /**
   * Performs an assertion by calling a function and return an Error upon failure.
   *
   * @param {Function} fn
   * @param {Response} res
   * @return {?Error}
   * @api private
   */// eslint-disable-next-line class-methods-use-this
  _assertFunction(fn, res) {
    let err;
    try {
      err = fn(res);
    } catch (e) {
      err = e;
    }
    if (err instanceof Error) return err;
  }
}

/**
 * Wraps an assert function into another.
 * The wrapper function edit the stack trace of any assertion error, prepending a more useful stack to it.
 *
 * @param {Function} assertFn
 * @returns {Function} wrapped assert function
 */

function wrapAssertFn(assertFn) {
  const savedStack = new Error().stack.split('\n').slice(3);

  return function(res) {
    let badStack;
    const err = assertFn(res);
    if (err instanceof Error && err.stack) {
      badStack = err.stack.replace(err.message, '').split('\n').slice(1);
      err.stack = [err.toString()]
        .concat(savedStack)
        .concat('----')
        .concat(badStack)
        .join('\n');
    }
    return err;
  };
}

/**
 * Return an `Error` with `msg` and results properties.
 *
 * @param {String} msg
 * @param {Mixed} expected
 * @param {Mixed} actual
 * @return {Error}
 * @api private
 */

function error(msg, expected, actual) {
  const err = new Error(msg);
  err.expected = expected;
  err.actual = actual;
  err.showDiff = true;
  return err;
}

/**
 * Expose `Test`.
 */

module.exports = Test;
<commit_msg>user super and arrow fn<commit_after>'use strict';

/**
 * Module dependencies.
 */

const { inspect } = require('util');
const { STATUS_CODES } = require('http');
const { Server } = require('https');
const { deepStrictEqual } = require('assert');
const { Request } = require('superagent');

class Test extends Request {
  /**
   * Initialize a new `Test` with the given `app`,
   * request `method` and `path`.
   *
   * @param {Server} app
   * @param {String} method
   * @param {String} path
   * @api public
   */
  constructor (app, method, path) {
    super(method.toUpperCase(), path);

    this.redirects(0);
    this.buffer();
    this.app = app;
    this._asserts = [];
    this.url = typeof app === 'string'
      ? app + path
      : this.serverAddress(app, path);
  }

  /**
   * Returns a URL, extracted from a server.
   *
   * @param {Server} app
   * @param {String} path
   * @returns {String} URL address
   * @api private
   */
  serverAddress(app, path) {
    const addr = app.address();

    if (!addr) this._server = app.listen(0);
    const port = app.address().port;
    const protocol = app instanceof Server ? 'https' : 'http';
    return protocol + '://127.0.0.1:' + port + path;
  }

  /**
   * Expectations:
   *
   *   .expect(200)
   *   .expect(200, fn)
   *   .expect(200, body)
   *   .expect('Some body')
   *   .expect('Some body', fn)
   *   .expect(['json array body', { key: 'val' }])
   *   .expect('Content-Type', 'application/json')
   *   .expect('Content-Type', 'application/json', fn)
   *   .expect(fn)
   *   .expect([200, 404])
   *
   * @return {Test}
   * @api public
   */
  expect(a, b, c) {
    // callback
    if (typeof a === 'function') {
      this._asserts.push(wrapAssertFn(a));
      return this;
    }
    if (typeof b === 'function') this.end(b);
    if (typeof c === 'function') this.end(c);

    // status
    if (typeof a === 'number') {
      this._asserts.push(wrapAssertFn(this._assertStatus.bind(this, a)));
      // body
      if (typeof b !== 'function' && arguments.length > 1) {
        this._asserts.push(wrapAssertFn(this._assertBody.bind(this, b)));
      }
      return this;
    }

    // multiple statuses
    if (Array.isArray(a) && a.length > 0 && a.every(val => typeof val === 'number')) {
      this._asserts.push(wrapAssertFn(this._assertStatusArray.bind(this, a)));
      return this;
    }

    // header field
    if (typeof b === 'string' || typeof b === 'number' || b instanceof RegExp) {
      this._asserts.push(wrapAssertFn(this._assertHeader.bind(this, { name: '' + a, value: b })));
      return this;
    }

    // body
    this._asserts.push(wrapAssertFn(this._assertBody.bind(this, a)));

    return this;
  }

  /**
   * Defer invoking superagent's `.end()` until
   * the server is listening.
   *
   * @param {Function} fn
   * @api public
   */
  end(fn) {
    const server = this._server;

    super.end((err, res) => {
      const localAssert = () => {
        this.assert(err, res, fn);
      };

      if (server && server._handle) return server.close(localAssert);

      localAssert();
    });

    return this;
  }

  /**
   * Perform assertions and invoke `fn(err, res)`.
   *
   * @param {?Error} resError
   * @param {Response} res
   * @param {Function} fn
   * @api private
   */
  assert(resError, res, fn) {
    let errorObj;

    // check for unexpected network errors or server not running/reachable errors
    // when there is no response and superagent sends back a System Error
    // do not check further for other asserts, if any, in such case
    // https://nodejs.org/api/errors.html#errors_common_system_errors
    const sysErrors = {
      ECONNREFUSED: 'Connection refused',
      ECONNRESET: 'Connection reset by peer',
      EPIPE: 'Broken pipe',
      ETIMEDOUT: 'Operation timed out'
    };

    if (!res && resError) {
      if (resError instanceof Error && resError.syscall === 'connect'
        && Object.getOwnPropertyNames(sysErrors).indexOf(resError.code) >= 0) {
        errorObj = new Error(resError.code + ': ' + sysErrors[resError.code]);
      } else {
        errorObj = resError;
      }
    }

    // asserts
    for (let i = 0; i < this._asserts.length && !errorObj; i += 1) {
      errorObj = this._assertFunction(this._asserts[i], res);
    }

    // set unexpected superagent error if no other error has occurred.
    if (!errorObj && resError instanceof Error && (!res || resError.status !== res.status)) {
      errorObj = resError;
    }

    fn.call(this, errorObj || null, res);
  }

  /**
   * Perform assertions on a response body and return an Error upon failure.
   *
   * @param {Mixed} body
   * @param {Response} res
   * @return {?Error}
   * @api private
   */// eslint-disable-next-line class-methods-use-this
  _assertBody(body, res) {
    const isRegexp = body instanceof RegExp;

    // parsed
    if (typeof body === 'object' && !isRegexp) {
      try {
        deepStrictEqual(body, res.body);
      } catch (err) {
        const a = inspect(body);
        const b = inspect(res.body);
        return error('expected ' + a + ' response body, got ' + b, body, res.body);
      }
    } else if (body !== res.text) {
      // string
      const a = inspect(body);
      const b = inspect(res.text);

      // regexp
      if (isRegexp) {
        if (!body.test(res.text)) {
          return error('expected body ' + b + ' to match ' + body, body, res.body);
        }
      } else {
        return error('expected ' + a + ' response body, got ' + b, body, res.body);
      }
    }
  }

  /**
   * Perform assertions on a response header and return an Error upon failure.
   *
   * @param {Object} header
   * @param {Response} res
   * @return {?Error}
   * @api private
   */// eslint-disable-next-line class-methods-use-this
  _assertHeader(header, res) {
    const field = header.name;
    const actual = res.header[field.toLowerCase()];
    const fieldExpected = header.value;

    if (typeof actual === 'undefined') return new Error('expected "' + field + '" header field');
    // This check handles header values that may be a String or single element Array
    if ((Array.isArray(actual) && actual.toString() === fieldExpected)
      || fieldExpected === actual) {
      return;
    }
    if (fieldExpected instanceof RegExp) {
      if (!fieldExpected.test(actual)) {
        return new Error('expected "' + field + '" matching '
          + fieldExpected + ', got "' + actual + '"');
      }
    } else {
      return new Error('expected "' + field + '" of "' + fieldExpected + '", got "' + actual + '"');
    }
  }

  /**
   * Perform assertions on the response status and return an Error upon failure.
   *
   * @param {Number} status
   * @param {Response} res
   * @return {?Error}
   * @api private
   */// eslint-disable-next-line class-methods-use-this
  _assertStatus(status, res) {
    if (res.status !== status) {
      const a = STATUS_CODES[status];
      const b = STATUS_CODES[res.status];
      return new Error('expected ' + status + ' "' + a + '", got ' + res.status + ' "' + b + '"');
    }
  }

  /**
   * Perform assertions on the response status and return an Error upon failure.
   *
   * @param {Array<Number>} statusArray
   * @param {Response} res
   * @return {?Error}
   * @api private
   */// eslint-disable-next-line class-methods-use-this
  _assertStatusArray(statusArray, res) {
    if (!statusArray.includes(res.status)) {
      const b = STATUS_CODES[res.status];
      const expectedList = statusArray.join(', ');
      return new Error(
        'expected one of "' + expectedList + '", got ' + res.status + ' "' + b + '"'
      );
    }
  }

  /**
   * Performs an assertion by calling a function and return an Error upon failure.
   *
   * @param {Function} fn
   * @param {Response} res
   * @return {?Error}
   * @api private
   */// eslint-disable-next-line class-methods-use-this
  _assertFunction(fn, res) {
    let err;
    try {
      err = fn(res);
    } catch (e) {
      err = e;
    }
    if (err instanceof Error) return err;
  }
}

/**
 * Wraps an assert function into another.
 * The wrapper function edit the stack trace of any assertion error, prepending a more useful stack to it.
 *
 * @param {Function} assertFn
 * @returns {Function} wrapped assert function
 */

function wrapAssertFn(assertFn) {
  const savedStack = new Error().stack.split('\n').slice(3);

  return function(res) {
    let badStack;
    const err = assertFn(res);
    if (err instanceof Error && err.stack) {
      badStack = err.stack.replace(err.message, '').split('\n').slice(1);
      err.stack = [err.toString()]
        .concat(savedStack)
        .concat('----')
        .concat(badStack)
        .join('\n');
    }
    return err;
  };
}

/**
 * Return an `Error` with `msg` and results properties.
 *
 * @param {String} msg
 * @param {Mixed} expected
 * @param {Mixed} actual
 * @return {Error}
 * @api private
 */

function error(msg, expected, actual) {
  const err = new Error(msg);
  err.expected = expected;
  err.actual = actual;
  err.showDiff = true;
  return err;
}

/**
 * Expose `Test`.
 */

module.exports = Test;
<|endoftext|>


<commit_before>var
	Chat = require('./Chat'),
	ca = new Chat.Client();

ca.on('listening', function(){
	ca.updateClientCache();
	console.log(ca._uuid);
})

ca.on('chat-message', function(uuid){

	messages = ca.message_history[uuid];

	console.log(uuid);

});<commit_msg>Deleted unnecessary file<commit_after><|endoftext|>


<commit_before>JAR.register({
    MID: 'jar.lang',
    deps: {
        System: ['.', '!']
    },
    bundle: ['Array.*', 'Date', 'Class', 'Function.*', 'I$Comparable', 'I$Iterable', 'Interface', 'M$Cloneable', 'M$Destructable', 'MixIn', 'Object.*', 'String']
}, function(System, config) {
    'use strict';

    var jar = this,
        sandboxes = {},
        container = document.documentElement,
        __SANDBOX__ = '__SANDBOX__',
        hasOwn = {}.hasOwnProperty,
        slice = [].slice,
        nativeTypes = {},
        nativeTypeSandbox, lang;

    /**
     * @namespace jar.lang
     */
    lang = {
        /**
         * @access public
         *
         * @memberof jar.lang
         *
         * @param {string} typeString
         * @param {Object} prototypeMethods
         * @param {Object} staticMethods
         *
         * @return {Object}
         */
        extendNativeType: function(typeString, prototypeMethods, staticMethods) {
            var Type = getNativeType(typeString),
                typePrototype = Type.prototype,
                methodName;

            for (methodName in prototypeMethods) {
                if (hasOwn.call(prototypeMethods, methodName)) {
                    if (!hasOwn.call(typePrototype, methodName)) {
                        typePrototype[methodName] = prototypeMethods[methodName];
                    }

                    hasOwn.call(Type, methodName) || (Type[methodName] = createDelegate(Type, methodName));
                }
            }

            for (methodName in staticMethods) {
                if (hasOwn.call(staticMethods, methodName) && !hasOwn.call(Type, methodName)) {
                    Type[methodName] = staticMethods[methodName];
                }
            }

            return Type;
        },
        /**
         * @access public
         *
         * @memberof jar.lang
         *
         * @param {String} typeName
         * @param {*} object
         * @param {String} methodName
         *
         * @throws {TypeError}
         */
        throwErrorIfNotSet: function(typeName, object, methodName) {
            if (!System.isSet(object)) {
                throwTypeError(typeName + '.prototype.' + methodName + ' called on null or undefined');
            }
        },
        /**
         * @access public
         *
         * @memberof jar.lang
         *
         * @param {Function} callback
         *
         * @throws {TypeError}
         */
        throwErrorIfNoFunction: function(callback) {
            if (!System.isFunction(callback)) {
                throwTypeError(callback + ' is not a function');
            }
        },
        /**
         * @access public
         *
         * @memberof jar.lang
         *
         * @param {String} typeName
         * @param {Boolean} isValueSet
         *
         * @throws {TypeError}
         */
        throwErrorIfNoValueSet: function(typeName, isValueSet) {
            if (!isValueSet) {
                throwTypeError('Reduce of empty ' + typeName + ' with no initial value');
            }
        },
        /**
         * @access public
         *
         * @memberof jar.lang
         *
         * @param {String} formatString
         *
         * @return {String}
         */
        generateHash: function(formatString) {
            return formatString.replace(/x/g, randomHex);
        },

        /**
         * @access public
         *
         * @memberOf jar.lang
         *
         * @param {String} domain
         *
         * @return {jar.lang~Sandbox}
         */
        sandbox: function(domain) {
            return sandboxes[domain] || (sandboxes[domain] = new Sandbox(domain));
        }
    };

    /**
     * @access private
     *
     * @memberof jar.lang
     * @inner
     *
     * @param {String} message
     *
     * @throws {TypeError}
     */
    function throwTypeError(message) {
        throw TypeError(message);
    }

    /**
     * @access private
     *
     * @memberOf jar.lang
     * @inner
     *
     * @param {Object} Type
     * @param {string} methodName
     * @param {*} targetObject
     * @param {Array} args
     *
     * @return {*}
     */
    function callNativeTypeMethod(Type, methodName, targetObject, args) {
        var callingObject;

        if (targetObject[methodName] === Type.prototype[methodName]) {
            callingObject = targetObject;
        }
        else {
            callingObject = Type.prototype;
        }

        return callingObject[methodName].apply(targetObject, args);
    }

    /**
     * @access private
     *
     * @memberOf jar.lang
     * @inner
     *
     * @param {Object} delegateType
     * @param {string} methodName
     *
     * @return {function(*):*}
     */
    function createDelegate(delegateType, methodName) {
        /**
         *
         * @param {*} targetObject
         *
         * @return {*}
         */
        function delegater(targetObject) {
            return callNativeTypeMethod(delegateType, methodName, targetObject, slice.call(arguments, 1));
        }

        return delegater;
    }

    /**
     * @access private
     *
     * @memberOf jar.lang
     * @inner
     *
     * @param {HTMLDocument} sandboxDoc
     * @param {string} scriptText
     */
    function createSandboxScript(sandboxDoc, scriptText) {
        var sandboxScript = sandboxDoc.createElement('script');

        sandboxScript.type = 'text/javascript';
        sandboxScript.text = scriptText;

        sandboxDoc.body.appendChild(sandboxScript);
    }

    /**
     *
Hack to steal native objects like Object, Array, String, etc. from an iframe
     * We save the native object in an iframe as a property of the window object
     * and then access this property for example to extend the Object.prototype
     * The Object.prototype of the current document won't be affected by this.

     *
     *
Note: the iframe has to be open all the time to make sure
     * that the current document has access to the native copies in some browsers.

     *
     *
You can read more about this {@link http://dean.edwards.name/weblog/2006/11/hooray/|here}

     *
     * @todo check browser support (should work in all legacy browsers)
     *
     * @access private
     *
     * @memberof jar.lang
     * @inner
     *
     * @class Sandbox
     *
     * @param {string} domain
     */
    function Sandbox(domain) {
        var sandbox = this,
            iframe = document.createElement('iframe'),
            sandboxWindow, sandboxDoc;

        iframe.style.display = 'none';
        iframe.id = domain;
        container.appendChild(iframe);
        sandboxWindow = iframe.contentWindow;
        sandboxDoc = sandboxWindow.document;
        //fire the onload event of the iframe
        //this is necessary so that the iframe doesn't block window.onload of the main page
        //found on http://www.aaronpeters.nl/blog/iframe-loading-techniques-performance?%3E
        sandboxDoc.open();
        //iframe onload happens after document.close()
        sandboxDoc.close();

        createSandboxScript(sandboxDoc, 'window.' + __SANDBOX__ + '={}');

        sandbox[__SANDBOX__] = sandboxWindow[__SANDBOX__];
        sandbox.doc = sandboxDoc;
    }

    /**
     * @access public
     *
     * @method add
     * @memberof jar.lang~Sandbox#
     *
     * @param {string} value
     *
     * @return {*}
     */
    Sandbox.prototype.add = function(value) {
        var sandbox = this,
            sandboxVars = sandbox[__SANDBOX__],
            sandboxedVar, accessor;

        if (System.isString(value)) {
            accessor = encodeURI(value);

            if (!(accessor in sandboxVars)) {
                createSandboxScript(sandbox.doc, __SANDBOX__ + '["' + accessor + '"]=' + value);
            }

            sandboxedVar = sandboxVars[accessor];
        }

        return sandboxedVar;
    };

    /**
     * @access public
     *
     * @method remove
     * @memberof jar.lang~Sandbox#
     *
     * @param {string} value
     */
    Sandbox.prototype.remove = function(value) {
        var sandbox = this,
            sandboxVars = sandbox[__SANDBOX__],
            accessor;

        if (System.isString(value)) {
            accessor = encodeURI(value);

            if (accessor in sandboxVars) {
                delete sandboxVars[accessor];
            }
        }
    };

    nativeTypeSandbox = lang.sandbox('__SYSTEM__');

	/**
	 * @access private
	 *
	 * @memberof jar.lang
	 * @inner
	 *
	 * @param {String} typeString
	 *
	 * @return {Object}
	 */
    function getNativeType(typeString) {
        var Type = nativeTypes[typeString] || (config('allowProtoOverride') ? window[typeString] : nativeTypeSandbox.add(typeString));

        if (!nativeTypes[typeString]) {

            makePluggable(typeString, Type);

            nativeTypes[typeString] = Type;
        }

        return Type;
    }

	/**
	 * @access private
	 *
	 * @memberof jar.lang
	 * @inner
	 *
	 * @param {String} typeString
	 * @param {Object} Type
	 */
    function makePluggable(typeString, Type) {
        var moduleName = jar.getCurrentModuleName(),
            subModuleName = moduleName + '.' + typeString + '-';

        Type.$plugIn = function(pluginRequest) {
            var extensions = pluginRequest.data.split('|'),
                extLen = extensions.length,
                idx = 0;

            if (extensions[0] === 'all') {
                extensions = [moduleName + '.*'];
            }
            else {
                while (idx < extLen && extensions[idx]) {
                    extensions[idx] = subModuleName + extensions[idx++];
                }
            }

            pluginRequest.$importAndLink(extensions, function() {
                pluginRequest.onSuccess(Type);
            }, pluginRequest.onError);
        };
    }

    /**
     * @access private
     *
     * @memberOf jar.lang
     * @inner
     *
     * @return {String}
     */
    function randomHex() {
        return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
    }

    return lang;
});<commit_msg>Minor changes; better errormessage if plugin fails<commit_after>JAR.register({
    MID: 'jar.lang',
    deps: {
        System: ['.', '!']
    },
    bundle: ['Array.*', 'Date', 'Class', 'Function.*', 'I$Comparable', 'I$Iterable', 'Interface', 'M$Cloneable', 'M$Destructable', 'MixIn', 'Object.*', 'String']
}, function(System, config) {
    'use strict';

    var jar = this,
        sandboxes = {},
        container = document.documentElement,
        __SANDBOX__ = '__SANDBOX__',
        hasOwn = {}.hasOwnProperty,
        slice = [].slice,
        nativeTypes = {},
        nativeTypeSandbox, lang;

    function hasOwnProp(object, property) {
        return hasOwn.call(object, property);
    }

    /**
     * @namespace jar.lang
     */
    lang = {
        /**
         * @access public
         *
         * @memberof jar.lang
         *
         * @param {string} typeString
         * @param {Object} prototypeMethods
         * @param {Object} staticMethods
         *
         * @return {Object}
         */
        extendNativeType: function(typeString, prototypeMethods, staticMethods) {
            var Type = getNativeType(typeString),
                typePrototype = Type.prototype,
                methodName;

            for (methodName in prototypeMethods) {
                if (hasOwnProp(prototypeMethods, methodName)) {
                    if (!hasOwnProp(typePrototype, methodName)) {
                        typePrototype[methodName] = prototypeMethods[methodName];
                    }

                    hasOwnProp(Type, methodName) || (Type[methodName] = createDelegate(Type, methodName));
                }
            }

            for (methodName in staticMethods) {
                if (hasOwnProp(staticMethods, methodName) && !hasOwnProp(Type, methodName)) {
                    Type[methodName] = staticMethods[methodName];
                }
            }

            return Type;
        },
        /**
         * @access public
         *
         * @memberof jar.lang
         *
         * @param {String} typeName
         * @param {*} object
         * @param {String} methodName
         *
         * @throws {TypeError}
         */
        throwErrorIfNotSet: function(typeName, object, methodName) {
            if (!System.isSet(object)) {
                throwTypeError(typeName + '.prototype.' + methodName + ' called on null or undefined');
            }
        },
        /**
         * @access public
         *
         * @memberof jar.lang
         *
         * @param {Function} callback
         *
         * @throws {TypeError}
         */
        throwErrorIfNoFunction: function(callback) {
            if (!System.isFunction(callback)) {
                throwTypeError(callback + ' is not a function');
            }
        },
        /**
         * @access public
         *
         * @memberof jar.lang
         *
         * @param {String} typeName
         * @param {Boolean} isValueSet
         *
         * @throws {TypeError}
         */
        throwErrorIfNoValueSet: function(typeName, isValueSet) {
            if (!isValueSet) {
                throwTypeError('Reduce of empty ' + typeName + ' with no initial value');
            }
        },
        /**
         * @access public
         *
         * @memberof jar.lang
         *
         * @param {String} formatString
         *
         * @return {String}
         */
        generateHash: function(formatString) {
            return formatString.replace(/x/g, randomHex);
        },

        /**
         * @access public
         *
         * @memberOf jar.lang
         *
         * @param {String} domain
         *
         * @return {jar.lang~Sandbox}
         */
        sandbox: function(domain) {
            return sandboxes[domain] || (sandboxes[domain] = new Sandbox(domain));
        }
    };

    /**
     * @access private
     *
     * @memberof jar.lang
     * @inner
     *
     * @param {String} message
     *
     * @throws {TypeError}
     */
    function throwTypeError(message) {
        throw TypeError(message);
    }

    /**
     * @access private
     *
     * @memberOf jar.lang
     * @inner
     *
     * @param {Object} Type
     * @param {string} methodName
     * @param {*} targetObject
     * @param {Array} args
     *
     * @return {*}
     */
    function callNativeTypeMethod(Type, methodName, targetObject, args) {
        var callingObject;

        if (targetObject[methodName] === Type.prototype[methodName]) {
            callingObject = targetObject;
        }
        else {
            callingObject = Type.prototype;
        }

        return callingObject[methodName].apply(targetObject, args);
    }

    /**
     * @access private
     *
     * @memberOf jar.lang
     * @inner
     *
     * @param {Object} delegateType
     * @param {string} methodName
     *
     * @return {function(*):*}
     */
    function createDelegate(delegateType, methodName) {
        /**
         *
         * @param {*} targetObject
         *
         * @return {*}
         */
        function delegater(targetObject) {
            return callNativeTypeMethod(delegateType, methodName, targetObject, slice.call(arguments, 1));
        }

        return delegater;
    }

    /**
     * @access private
     *
     * @memberOf jar.lang
     * @inner
     *
     * @param {HTMLDocument} sandboxDoc
     * @param {string} scriptText
     */
    function createSandboxScript(sandboxDoc, scriptText) {
        var sandboxScript = sandboxDoc.createElement('script');

        sandboxScript.type = 'text/javascript';
        sandboxScript.text = scriptText;

        sandboxDoc.body.appendChild(sandboxScript);
    }

    /**
     *
Hack to steal native objects like Object, Array, String, etc. from an iframe
     * We save the native object in an iframe as a property of the window object
     * and then access this property for example to extend the Object.prototype
     * The Object.prototype of the current document won't be affected by this.

     *
     *
Note: the iframe has to be open all the time to make sure
     * that the current document has access to the native copies in some browsers.

     *
     *
You can read more about this {@link http://dean.edwards.name/weblog/2006/11/hooray/|here}

     *
     * @todo check browser support (should work in all legacy browsers)
     *
     * @access private
     *
     * @memberof jar.lang
     * @inner
     *
     * @class Sandbox
     *
     * @param {string} domain
     */
    function Sandbox(domain) {
        var sandbox = this,
            iframe = document.createElement('iframe'),
            sandboxWindow, sandboxDoc;

        iframe.style.display = 'none';
        iframe.id = domain;
        container.appendChild(iframe);
        sandboxWindow = iframe.contentWindow;
        sandboxDoc = sandboxWindow.document;
        //fire the onload event of the iframe
        //this is necessary so that the iframe doesn't block window.onload of the main page
        //found on http://www.aaronpeters.nl/blog/iframe-loading-techniques-performance?%3E
        sandboxDoc.open();
        //iframe onload happens after document.close()
        sandboxDoc.close();

        createSandboxScript(sandboxDoc, 'window.' + __SANDBOX__ + '={}');

        sandbox[__SANDBOX__] = sandboxWindow[__SANDBOX__];
        sandbox.doc = sandboxDoc;
    }

    /**
     * @access public
     *
     * @method add
     * @memberof jar.lang~Sandbox#
     *
     * @param {string} value
     *
     * @return {*}
     */
    Sandbox.prototype.add = function(value) {
        var sandbox = this,
            sandboxVars = sandbox[__SANDBOX__],
            sandboxedVar, accessor;

        if (System.isString(value)) {
            accessor = encodeURI(value);

            if (!hasOwnProp(sandboxVars, accessor)) {
                createSandboxScript(sandbox.doc, __SANDBOX__ + '["' + accessor + '"]=' + value);
            }

            sandboxedVar = sandboxVars[accessor];
        }

        return sandboxedVar;
    };

    /**
     * @access public
     *
     * @method remove
     * @memberof jar.lang~Sandbox#
     *
     * @param {string} value
     */
    Sandbox.prototype.remove = function(value) {
        var sandbox = this,
            sandboxVars = sandbox[__SANDBOX__],
            accessor;

        if (System.isString(value)) {
            accessor = encodeURI(value);

            if (hasOwnProp(sandboxVars, accessor)) {
                delete sandboxVars[accessor];
            }
        }
    };

    nativeTypeSandbox = lang.sandbox('__SYSTEM__');

    /**
     * @access private
     *
     * @memberof jar.lang
     * @inner
     *
     * @param {String} typeString
     *
     * @return {Object}
     */
    function getNativeType(typeString) {
        var Type = nativeTypes[typeString] || (config('allowProtoOverride') ? window[typeString] : nativeTypeSandbox.add(typeString));

        if (!nativeTypes[typeString]) {

            makePluggable(typeString, Type);

            nativeTypes[typeString] = Type;
        }

        return Type;
    }

    /**
     * @access private
     *
     * @memberof jar.lang
     * @inner
     *
     * @param {String} typeString
     * @param {Object} Type
     */
    function makePluggable(typeString, Type) {
        var moduleName = jar.getCurrentModuleName(),
            subModuleName = moduleName + '.' + typeString + '-';

        Type.$plugIn = function(pluginRequest) {
            var extensions = pluginRequest.data.split('|'),
                extLen = extensions.length,
                idx = 0;

            if (extensions[0] === 'all') {
                extensions = [moduleName + '.*'];
            }
            else {
                while (idx < extLen) {
                    extensions[idx] = subModuleName + extensions[idx++];
                }
            }

            pluginRequest.$importAndLink(extensions, function() {
                pluginRequest.onSuccess(Type);
            }, function(extensionName) {
                pluginRequest.onError('Could not import the extension "' + extensionName + '" requested by "' + pluginRequest.listener + '"');
            });
        };
    }

    /**
     * @access private
     *
     * @memberOf jar.lang
     * @inner
     *
     * @return {String}
     */
    function randomHex() {
        return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
    }

    return lang;
});<|endoftext|>


<commit_before>/**
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @flow
 * @format
 * @generate-docs
 */

'use strict';

const Platform = require('../../Utilities/Platform');
const React = require('react');
const StyleSheet = require('../../StyleSheet/StyleSheet');

import type {ColorValue} from '../../StyleSheet/StyleSheetTypes';
import type {SyntheticEvent} from '../../Types/CoreEventTypes';
import type {ViewProps} from '../View/ViewPropTypes';
import SwitchNativeComponent from './SwitchNativeComponent';
import AndroidSwitchNativeComponent from './AndroidSwitchNativeComponent';

type SwitchChangeEvent = SyntheticEvent<
  $ReadOnly<{|
    value: boolean,
  |}>,
>;

export type Props = $ReadOnly<{|
  ...ViewProps,

  /**
   * Whether the switch is disabled. Defaults to false.
   */
  disabled?: ?boolean,

  /**
   * Boolean value of the switch. Defaults to false.
   */
  value?: ?boolean,

  /**
   * Custom color for the switch thumb.
   */
  thumbColor?: ?ColorValue,

  /**
   * Custom colors for the switch track.
   *
   * NOTE: On iOS when the switch value is false, the track shrinks into the
   * border. If you want to change the color of the background exposed by the
   * shrunken track, use `ios_backgroundColor`.
   */
  trackColor?: ?$ReadOnly<{|
    false?: ?ColorValue,
    true?: ?ColorValue,
  |}>,

  /**
   * On iOS, custom color for the background. This background color can be seen
   * either when the switch value is false or when the switch is disabled (and
   * the switch is translucent).
   */
  ios_backgroundColor?: ?ColorValue,

  /**
   * Called when the user tries to change the value of the switch.
   *
   * Receives the change event as an argument. If you want to only receive the
   * new value, use `onValueChange` instead.
   */
  onChange?: ?(event: SwitchChangeEvent) => Promise<void> | void,

  /**
   * Called when the user tries to change the value of the switch.
   *
   * Receives the new value as an argument. If you want to instead receive an
   * event, use `onChange`.
   */
  onValueChange?: ?(value: boolean) => Promise<void> | void,
|}>;

/**
 * A visual toggle between two mutually exclusive states.
 *
 * This is a controlled component that requires an `onValueChange` callback that
 * updates the `value` prop in order for the component to reflect user actions.
 * If the `value` prop is not updated, the component will continue to render the
 * supplied `value` prop instead of the expected result of any user actions.
 */
class Switch extends React.Component<Props> {
  _nativeSwitchRef: ?React.ElementRef<
    typeof SwitchNativeComponent | typeof AndroidSwitchNativeComponent,
  >;
  _lastNativeValue: ?boolean;

  render(): React.Node {
    const {
      disabled,
      ios_backgroundColor,
      onChange,
      onValueChange,
      style,
      thumbColor,
      trackColor,
      value,
      ...props
    } = this.props;

    // Support deprecated color props.
    let _thumbColor = thumbColor;
    let _trackColorForFalse = trackColor?.false;
    let _trackColorForTrue = trackColor?.true;

    // TODO: Remove support for these props after a couple releases.
    const {thumbTintColor, tintColor, onTintColor} = (props: $FlowFixMe);
    if (thumbTintColor != null) {
      _thumbColor = thumbTintColor;
      if (__DEV__) {
        console.warn(
          'Switch: `thumbTintColor` is deprecated, use `thumbColor` instead.',
        );
      }
    }
    if (tintColor != null) {
      _trackColorForFalse = tintColor;
      if (__DEV__) {
        console.warn(
          'Switch: `tintColor` is deprecated, use `trackColor` instead.',
        );
      }
    }
    if (onTintColor != null) {
      _trackColorForTrue = onTintColor;
      if (__DEV__) {
        console.warn(
          'Switch: `onTintColor` is deprecated, use `trackColor` instead.',
        );
      }
    }

    if (Platform.OS === 'android') {
      const platformProps = {
        enabled: disabled !== true,
        on: value === true,
        style,
        thumbTintColor: _thumbColor,
        trackColorForFalse: _trackColorForFalse,
        trackColorForTrue: _trackColorForTrue,
        trackTintColor:
          value === true ? _trackColorForTrue : _trackColorForFalse,
      };

      return (
        <AndroidSwitchNativeComponent
          {...props}
          {...platformProps}
          accessibilityRole={props.accessibilityRole ?? 'button'}
          onChange={this._handleChange}
          onResponderTerminationRequest={returnsFalse}
          onStartShouldSetResponder={returnsTrue}
          ref={this._handleSwitchNativeComponentRef}
        />
      );
    }

    const platformProps = {
      disabled,
      onTintColor: _trackColorForTrue,
      style: StyleSheet.compose(
        {height: 31, width: 51},
        StyleSheet.compose(
          style,
          ios_backgroundColor == null
            ? null
            : {
                backgroundColor: ios_backgroundColor,
                borderRadius: 16,
              },
        ),
      ),
      thumbTintColor: _thumbColor,
      tintColor: _trackColorForFalse,
      value: value === true,
    };

    return (
      <SwitchNativeComponent
        {...props}
        {...platformProps}
        accessibilityRole={props.accessibilityRole ?? 'button'}
        onChange={this._handleChange}
        onResponderTerminationRequest={returnsFalse}
        onStartShouldSetResponder={returnsTrue}
        ref={this._handleSwitchNativeComponentRef}
      />
    );
  }

  componentDidUpdate() {
    // This is necessary in case native updates the switch and JS decides
    // that the update should be ignored and we should stick with the value
    // that we have in JS.
    const nativeProps = {};
    const value = this.props.value === true;

    if (this._lastNativeValue !== value && typeof value === 'boolean') {
      nativeProps.value = value;
    }

    if (
      Object.keys(nativeProps).length > 0 &&
      this._nativeSwitchRef &&
      this._nativeSwitchRef.setNativeProps
    ) {
      this._nativeSwitchRef.setNativeProps(nativeProps);
    }
  }

  _handleChange = (event: SwitchChangeEvent) => {
    if (this.props.onChange != null) {
      this.props.onChange(event);
    }

    if (this.props.onValueChange != null) {
      this.props.onValueChange(event.nativeEvent.value);
    }

    this._lastNativeValue = event.nativeEvent.value;
    this.forceUpdate();
  };

  _handleSwitchNativeComponentRef = (
    ref: ?React.ElementRef<
      typeof SwitchNativeComponent | typeof AndroidSwitchNativeComponent,
    >,
  ) => {
    this._nativeSwitchRef = ref;
  };
}

const returnsFalse = () => false;
const returnsTrue = () => true;

module.exports = Switch;
<commit_msg>Fix Switch component accessibility role (#26899)<commit_after>/**
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @flow
 * @format
 * @generate-docs
 */

'use strict';

const Platform = require('../../Utilities/Platform');
const React = require('react');
const StyleSheet = require('../../StyleSheet/StyleSheet');

import type {ColorValue} from '../../StyleSheet/StyleSheetTypes';
import type {SyntheticEvent} from '../../Types/CoreEventTypes';
import type {ViewProps} from '../View/ViewPropTypes';
import SwitchNativeComponent from './SwitchNativeComponent';
import AndroidSwitchNativeComponent from './AndroidSwitchNativeComponent';

type SwitchChangeEvent = SyntheticEvent<
  $ReadOnly<{|
    value: boolean,
  |}>,
>;

export type Props = $ReadOnly<{|
  ...ViewProps,

  /**
   * Whether the switch is disabled. Defaults to false.
   */
  disabled?: ?boolean,

  /**
   * Boolean value of the switch. Defaults to false.
   */
  value?: ?boolean,

  /**
   * Custom color for the switch thumb.
   */
  thumbColor?: ?ColorValue,

  /**
   * Custom colors for the switch track.
   *
   * NOTE: On iOS when the switch value is false, the track shrinks into the
   * border. If you want to change the color of the background exposed by the
   * shrunken track, use `ios_backgroundColor`.
   */
  trackColor?: ?$ReadOnly<{|
    false?: ?ColorValue,
    true?: ?ColorValue,
  |}>,

  /**
   * On iOS, custom color for the background. This background color can be seen
   * either when the switch value is false or when the switch is disabled (and
   * the switch is translucent).
   */
  ios_backgroundColor?: ?ColorValue,

  /**
   * Called when the user tries to change the value of the switch.
   *
   * Receives the change event as an argument. If you want to only receive the
   * new value, use `onValueChange` instead.
   */
  onChange?: ?(event: SwitchChangeEvent) => Promise<void> | void,

  /**
   * Called when the user tries to change the value of the switch.
   *
   * Receives the new value as an argument. If you want to instead receive an
   * event, use `onChange`.
   */
  onValueChange?: ?(value: boolean) => Promise<void> | void,
|}>;

/**
 * A visual toggle between two mutually exclusive states.
 *
 * This is a controlled component that requires an `onValueChange` callback that
 * updates the `value` prop in order for the component to reflect user actions.
 * If the `value` prop is not updated, the component will continue to render the
 * supplied `value` prop instead of the expected result of any user actions.
 */
class Switch extends React.Component<Props> {
  _nativeSwitchRef: ?React.ElementRef<
    typeof SwitchNativeComponent | typeof AndroidSwitchNativeComponent,
  >;
  _lastNativeValue: ?boolean;

  render(): React.Node {
    const {
      disabled,
      ios_backgroundColor,
      onChange,
      onValueChange,
      style,
      thumbColor,
      trackColor,
      value,
      ...props
    } = this.props;

    // Support deprecated color props.
    let _thumbColor = thumbColor;
    let _trackColorForFalse = trackColor?.false;
    let _trackColorForTrue = trackColor?.true;

    // TODO: Remove support for these props after a couple releases.
    const {thumbTintColor, tintColor, onTintColor} = (props: $FlowFixMe);
    if (thumbTintColor != null) {
      _thumbColor = thumbTintColor;
      if (__DEV__) {
        console.warn(
          'Switch: `thumbTintColor` is deprecated, use `thumbColor` instead.',
        );
      }
    }
    if (tintColor != null) {
      _trackColorForFalse = tintColor;
      if (__DEV__) {
        console.warn(
          'Switch: `tintColor` is deprecated, use `trackColor` instead.',
        );
      }
    }
    if (onTintColor != null) {
      _trackColorForTrue = onTintColor;
      if (__DEV__) {
        console.warn(
          'Switch: `onTintColor` is deprecated, use `trackColor` instead.',
        );
      }
    }

    if (Platform.OS === 'android') {
      const platformProps = {
        enabled: disabled !== true,
        on: value === true,
        style,
        thumbTintColor: _thumbColor,
        trackColorForFalse: _trackColorForFalse,
        trackColorForTrue: _trackColorForTrue,
        trackTintColor:
          value === true ? _trackColorForTrue : _trackColorForFalse,
      };

      return (
        <AndroidSwitchNativeComponent
          {...props}
          {...platformProps}
          accessibilityRole={props.accessibilityRole ?? 'switch'}
          onChange={this._handleChange}
          onResponderTerminationRequest={returnsFalse}
          onStartShouldSetResponder={returnsTrue}
          ref={this._handleSwitchNativeComponentRef}
        />
      );
    }

    const platformProps = {
      disabled,
      onTintColor: _trackColorForTrue,
      style: StyleSheet.compose(
        {height: 31, width: 51},
        StyleSheet.compose(
          style,
          ios_backgroundColor == null
            ? null
            : {
                backgroundColor: ios_backgroundColor,
                borderRadius: 16,
              },
        ),
      ),
      thumbTintColor: _thumbColor,
      tintColor: _trackColorForFalse,
      value: value === true,
    };

    return (
      <SwitchNativeComponent
        {...props}
        {...platformProps}
        accessibilityRole={props.accessibilityRole ?? 'switch'}
        onChange={this._handleChange}
        onResponderTerminationRequest={returnsFalse}
        onStartShouldSetResponder={returnsTrue}
        ref={this._handleSwitchNativeComponentRef}
      />
    );
  }

  componentDidUpdate() {
    // This is necessary in case native updates the switch and JS decides
    // that the update should be ignored and we should stick with the value
    // that we have in JS.
    const nativeProps = {};
    const value = this.props.value === true;

    if (this._lastNativeValue !== value && typeof value === 'boolean') {
      nativeProps.value = value;
    }

    if (
      Object.keys(nativeProps).length > 0 &&
      this._nativeSwitchRef &&
      this._nativeSwitchRef.setNativeProps
    ) {
      this._nativeSwitchRef.setNativeProps(nativeProps);
    }
  }

  _handleChange = (event: SwitchChangeEvent) => {
    if (this.props.onChange != null) {
      this.props.onChange(event);
    }

    if (this.props.onValueChange != null) {
      this.props.onValueChange(event.nativeEvent.value);
    }

    this._lastNativeValue = event.nativeEvent.value;
    this.forceUpdate();
  };

  _handleSwitchNativeComponentRef = (
    ref: ?React.ElementRef<
      typeof SwitchNativeComponent | typeof AndroidSwitchNativeComponent,
    >,
  ) => {
    this._nativeSwitchRef = ref;
  };
}

const returnsFalse = () => false;
const returnsTrue = () => true;

module.exports = Switch;
<|endoftext|>


<commit_before>'use strict'

/**
  Parse milliseconds between two unix timestamps.

  @param {Number} from - timestamp to start from
  @param {Number} to - timestamp after elapsed time
  @returns {String} H:M:S passed
  */
const diff = (from, to) => fromMs(to - from)

/**
  Prepend a leading 0 to single-digit times.

  @param {Number} num - number to potentially prepend
  @returns {String|Number} num or prepended string
  */
const digit = num => num.toString().length === 1 ? `0${num}` : num

/**
  Convert milliseconds to hours, minutes, and seconds.

  @param {Number} ms - a number of milliseconds
  @returns {String} H:M:S
  */
const fromMs = ms =>
  [ Math.floor(ms / 3600000)
  , digit(Math.floor(ms / 60000 % 60))
  , digit(Math.floor(ms / 1000 % 60))
  ].join(':')

// TODO jsdoc
const fromOb = ob =>
  [ hours(ob.getHours())
  , digit(ob.getMinutes())
  , digit(ob.getSeconds())
  ].join(':')

/**
  Convert 24 hour notation to 12 hour notation.

  @param {Number} num - hours to convert
  @returns {Number} converted hours
  */
const hours = num => num > 12 ? num - 12 : num

//----------------------------------------------------------
// exports
//----------------------------------------------------------
module.exports =
  { diff
  , digit
  , fromMs
  , fromOb
  , hours
  }
<commit_msg>lib/time docstrings<commit_after>'use strict'

/**
  Parse milliseconds between two unix timestamps.

  @param {Number} from - timestamp to start from
  @param {Number} to - timestamp after elapsed time
  @returns {String} H:M:S passed
  */
const diff = (from, to) => fromMs(to - from)

/**
  Prepend a leading 0 to single-digit times.

  @param {Number} num - number to potentially prepend
  @returns {String|Number} num or prepended string
  */
const digit = num => num.toString().length === 1 ? `0${num}` : num

/**
  Convert milliseconds to hours, minutes, and seconds.

  @param {Number} ms - a number of milliseconds
  @returns {String} H:M:S
  */
const fromMs = ms =>
  [ Math.floor(ms / 3600000)
  , digit(Math.floor(ms / 60000 % 60))
  , digit(Math.floor(ms / 1000 % 60))
  ].join(':')

/**
  Convert Date object to hours, minutes, and seconds.

  @param {Object} ob - Date object
  @returns {String} H:M:S
 */
const fromOb = ob =>
  [ hours(ob.getHours())
  , digit(ob.getMinutes())
  , digit(ob.getSeconds())
  ].join(':')

/**
  Convert 24 hour notation to 12 hour notation.

  @param {Number} num - hours to convert
  @returns {Number} converted hours
  */
const hours = num => num > 12 ? num - 12 : num

//----------------------------------------------------------
// exports
//----------------------------------------------------------
module.exports =
  { diff
  , digit
  , fromMs
  , fromOb
  , hours
  }
<|endoftext|>


<commit_before>/**
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @flow strict-local
 * @format
 * @generate-docs
 */

import Platform from '../../Utilities/Platform';
import * as React from 'react';
import StyleSheet from '../../StyleSheet/StyleSheet';

import AndroidSwitchNativeComponent, {
  Commands as AndroidSwitchCommands,
} from './AndroidSwitchNativeComponent';
import SwitchNativeComponent, {
  Commands as SwitchCommands,
} from './SwitchNativeComponent';

import type {ColorValue} from '../../StyleSheet/StyleSheet';
import type {SyntheticEvent} from '../../Types/CoreEventTypes';
import type {ViewProps} from '../View/ViewPropTypes';

type SwitchChangeEvent = SyntheticEvent<
  $ReadOnly<{|
    value: boolean,
  |}>,
>;

export type Props = $ReadOnly<{|
  ...ViewProps,

  /**
    If true the user won't be able to toggle the switch.

    @default false
   */
  disabled?: ?boolean,

  /**
    The value of the switch. If true the switch will be turned on.

    @default false
   */
  value?: ?boolean,

  /**
    Color of the foreground switch grip. If this is set on iOS, the switch grip will lose its drop shadow.
   */
  thumbColor?: ?ColorValue,

  /**
    Custom colors for the switch track.

    _iOS_: When the switch value is false, the track shrinks into the border. If you want to change the
    color of the background exposed by the shrunken track, use
     [`ios_backgroundColor`](https://reactnative.dev/docs/switch#ios_backgroundColor).
   */
  trackColor?: ?$ReadOnly<{|
    false?: ?ColorValue,
    true?: ?ColorValue,
  |}>,

  /**
    On iOS, custom color for the background. This background color can be
    seen either when the switch value is false or when the switch is
    disabled (and the switch is translucent).
   */
  ios_backgroundColor?: ?ColorValue,

  /**
    Invoked when the user tries to change the value of the switch. Receives
    the change event as an argument. If you want to only receive the new
    value, use `onValueChange` instead.
   */
  onChange?: ?(event: SwitchChangeEvent) => Promise<void> | void,

  /**
    Invoked when the user tries to change the value of the switch. Receives
    the new value as an argument. If you want to instead receive an event,
    use `onChange`.
   */
  onValueChange?: ?(value: boolean) => Promise<void> | void,
|}>;
const returnsFalse = () => false;
const returnsTrue = () => true;

/**
  Renders a boolean input.

  This is a controlled component that requires an `onValueChange`
  callback that updates the `value` prop in order for the component to
  reflect user actions. If the `value` prop is not updated, the
  component will continue to render the supplied `value` prop instead of
  the expected result of any user actions.

  ```SnackPlayer name=Switch
  import React, { useState } from "react";
  import { View, Switch, StyleSheet } from "react-native";

  const App = () => {
    const [isEnabled, setIsEnabled] = useState(false);
    const toggleSwitch = () => setIsEnabled(previousState => !previousState);

    return (
      <View style={styles.container}>
        <Switch
          trackColor={{ false: "#767577", true: "#81b0ff" }}
          thumbColor={isEnabled ? "#f5dd4b" : "#f4f3f4"}
          ios_backgroundColor="#3e3e3e"
          onValueChange={toggleSwitch}
          value={isEnabled}
        />
      </View>
    );
  }

  const styles = StyleSheet.create({
    container: {
      flex: 1,
      alignItems: "center",
      justifyContent: "center"
    }
  });

  export default App;
  ```
 */
export default function Switch(props: Props): React.Node {
  const {
    disabled,
    ios_backgroundColor,
    onChange,
    onValueChange,
    style,
    thumbColor,
    trackColor,
    value,
    ...restProps
  } = props;
  const trackColorForFalse = trackColor?.false;
  const trackColorForTrue = trackColor?.true;

  const nativeSwitchRef = React.useRef<?React.ElementRef<
    typeof SwitchNativeComponent | typeof AndroidSwitchNativeComponent,
  >>(null);
  const [native, setNative] = React.useState({value: null});

  const handleChange = (event: SwitchChangeEvent) => {
    onChange?.(event);
    onValueChange?.(event.nativeEvent.value);
    setNative({value: event.nativeEvent.value});
  };

  React.useLayoutEffect(() => {
    // This is necessary in case native updates the switch and JS decides
    // that the update should be ignored and we should stick with the value
    // that we have in JS.
    const jsValue = value === true;
    const shouldUpdateNativeSwitch = native.value !== jsValue;
    if (
      shouldUpdateNativeSwitch &&
      nativeSwitchRef.current?.setNativeProps != null
    ) {
      if (Platform.OS === 'android') {
        AndroidSwitchCommands.setNativeValue(nativeSwitchRef.current, jsValue);
      } else {
        SwitchCommands.setValue(nativeSwitchRef.current, jsValue);
      }
    }
  }, [value, native]);

  if (Platform.OS === 'android') {
    const platformProps = {
      enabled: disabled !== true,
      on: value === true,
      style,
      thumbTintColor: thumbColor,
      trackColorForFalse: trackColorForFalse,
      trackColorForTrue: trackColorForTrue,
      trackTintColor: value === true ? trackColorForTrue : trackColorForFalse,
    };

    return (
      <AndroidSwitchNativeComponent
        {...restProps}
        {...platformProps}
        accessibilityRole={props.accessibilityRole ?? 'switch'}
        onChange={handleChange}
        onResponderTerminationRequest={returnsFalse}
        onStartShouldSetResponder={returnsTrue}
        ref={nativeSwitchRef}
      />
    );
  } else {
    const platformProps = {
      disabled,
      onTintColor: trackColorForTrue,
      style: StyleSheet.compose(
        {height: 31, width: 51},
        StyleSheet.compose(
          style,
          ios_backgroundColor == null
            ? null
            : {
                backgroundColor: ios_backgroundColor,
                borderRadius: 16,
              },
        ),
      ),
      thumbTintColor: thumbColor,
      tintColor: trackColorForFalse,
      value: value === true,
    };

    return (
      <SwitchNativeComponent
        {...restProps}
        {...platformProps}
        accessibilityRole={props.accessibilityRole ?? 'switch'}
        onChange={handleChange}
        onResponderTerminationRequest={returnsFalse}
        onStartShouldSetResponder={returnsTrue}
        ref={nativeSwitchRef}
      />
    );
  }
}
<commit_msg>Fix Switch ref forwarding (#31629)<commit_after>/**
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @flow strict-local
 * @format
 * @generate-docs
 */

import Platform from '../../Utilities/Platform';
import * as React from 'react';
import StyleSheet from '../../StyleSheet/StyleSheet';

import AndroidSwitchNativeComponent, {
  Commands as AndroidSwitchCommands,
} from './AndroidSwitchNativeComponent';
import SwitchNativeComponent, {
  Commands as SwitchCommands,
} from './SwitchNativeComponent';

import type {ColorValue} from '../../StyleSheet/StyleSheet';
import type {SyntheticEvent} from '../../Types/CoreEventTypes';
import type {ViewProps} from '../View/ViewPropTypes';

type SwitchChangeEvent = SyntheticEvent<
  $ReadOnly<{|
    value: boolean,
  |}>,
>;

export type Props = $ReadOnly<{|
  ...ViewProps,

  /**
    If true the user won't be able to toggle the switch.

    @default false
   */
  disabled?: ?boolean,

  /**
    The value of the switch. If true the switch will be turned on.

    @default false
   */
  value?: ?boolean,

  /**
    Color of the foreground switch grip. If this is set on iOS, the switch grip will lose its drop shadow.
   */
  thumbColor?: ?ColorValue,

  /**
    Custom colors for the switch track.

    _iOS_: When the switch value is false, the track shrinks into the border. If you want to change the
    color of the background exposed by the shrunken track, use
     [`ios_backgroundColor`](https://reactnative.dev/docs/switch#ios_backgroundColor).
   */
  trackColor?: ?$ReadOnly<{|
    false?: ?ColorValue,
    true?: ?ColorValue,
  |}>,

  /**
    On iOS, custom color for the background. This background color can be
    seen either when the switch value is false or when the switch is
    disabled (and the switch is translucent).
   */
  ios_backgroundColor?: ?ColorValue,

  /**
    Invoked when the user tries to change the value of the switch. Receives
    the change event as an argument. If you want to only receive the new
    value, use `onValueChange` instead.
   */
  onChange?: ?(event: SwitchChangeEvent) => Promise<void> | void,

  /**
    Invoked when the user tries to change the value of the switch. Receives
    the new value as an argument. If you want to instead receive an event,
    use `onChange`.
   */
  onValueChange?: ?(value: boolean) => Promise<void> | void,
|}>;
const returnsFalse = () => false;
const returnsTrue = () => true;

/**
  Renders a boolean input.

  This is a controlled component that requires an `onValueChange`
  callback that updates the `value` prop in order for the component to
  reflect user actions. If the `value` prop is not updated, the
  component will continue to render the supplied `value` prop instead of
  the expected result of any user actions.

  ```SnackPlayer name=Switch
  import React, { useState } from "react";
  import { View, Switch, StyleSheet } from "react-native";

  const App = () => {
    const [isEnabled, setIsEnabled] = useState(false);
    const toggleSwitch = () => setIsEnabled(previousState => !previousState);

    return (
      <View style={styles.container}>
        <Switch
          trackColor={{ false: "#767577", true: "#81b0ff" }}
          thumbColor={isEnabled ? "#f5dd4b" : "#f4f3f4"}
          ios_backgroundColor="#3e3e3e"
          onValueChange={toggleSwitch}
          value={isEnabled}
        />
      </View>
    );
  }

  const styles = StyleSheet.create({
    container: {
      flex: 1,
      alignItems: "center",
      justifyContent: "center"
    }
  });

  export default App;
  ```
 */

const SwitchWithForwardedRef: React.AbstractComponent<
  Props,
  React.ElementRef<
    typeof SwitchNativeComponent | typeof AndroidSwitchNativeComponent,
  >,
> = React.forwardRef(function Switch(props, forwardedRef): React.Node {
  const {
    disabled,
    ios_backgroundColor,
    onChange,
    onValueChange,
    style,
    thumbColor,
    trackColor,
    value,
    ...restProps
  } = props;
  const trackColorForFalse = trackColor?.false;
  const trackColorForTrue = trackColor?.true;

  const nativeSwitchRef = React.useRef<React.ElementRef<
    typeof SwitchNativeComponent | typeof AndroidSwitchNativeComponent,
  > | null>(null);
  React.useImperativeHandle(forwardedRef, () => nativeSwitchRef.current);

  const [native, setNative] = React.useState({value: null});

  const handleChange = (event: SwitchChangeEvent) => {
    onChange?.(event);
    onValueChange?.(event.nativeEvent.value);
    setNative({value: event.nativeEvent.value});
  };

  React.useLayoutEffect(() => {
    // This is necessary in case native updates the switch and JS decides
    // that the update should be ignored and we should stick with the value
    // that we have in JS.
    const jsValue = value === true;
    const shouldUpdateNativeSwitch = native.value !== jsValue;
    if (
      shouldUpdateNativeSwitch &&
      nativeSwitchRef.current?.setNativeProps != null
    ) {
      if (Platform.OS === 'android') {
        AndroidSwitchCommands.setNativeValue(nativeSwitchRef.current, jsValue);
      } else {
        SwitchCommands.setValue(nativeSwitchRef.current, jsValue);
      }
    }
  }, [value, native]);

  if (Platform.OS === 'android') {
    const platformProps = {
      enabled: disabled !== true,
      on: value === true,
      style,
      thumbTintColor: thumbColor,
      trackColorForFalse: trackColorForFalse,
      trackColorForTrue: trackColorForTrue,
      trackTintColor: value === true ? trackColorForTrue : trackColorForFalse,
    };

    return (
      <AndroidSwitchNativeComponent
        {...restProps}
        {...platformProps}
        accessibilityRole={props.accessibilityRole ?? 'switch'}
        onChange={handleChange}
        onResponderTerminationRequest={returnsFalse}
        onStartShouldSetResponder={returnsTrue}
        ref={nativeSwitchRef}
      />
    );
  } else {
    const platformProps = {
      disabled,
      onTintColor: trackColorForTrue,
      style: StyleSheet.compose(
        {height: 31, width: 51},
        StyleSheet.compose(
          style,
          ios_backgroundColor == null
            ? null
            : {
                backgroundColor: ios_backgroundColor,
                borderRadius: 16,
              },
        ),
      ),
      thumbTintColor: thumbColor,
      tintColor: trackColorForFalse,
      value: value === true,
    };

    return (
      <SwitchNativeComponent
        {...restProps}
        {...platformProps}
        accessibilityRole={props.accessibilityRole ?? 'switch'}
        onChange={handleChange}
        onResponderTerminationRequest={returnsFalse}
        onStartShouldSetResponder={returnsTrue}
        ref={nativeSwitchRef}
      />
    );
  }
});

export default SwitchWithForwardedRef;
<|endoftext|>