<commit_before>/* simple postMessage
 * by Leonardo Dutra (https://github.com/LeoDutra)
 * Dual licensed under the MIT and GPL licenses.
 *
 * based on
 *
 * ender postMessage - v0.1.3 - 5/1/2012
 * by Thomas Sturm http://www.sturm.to
 * Dual licensed under the MIT and GPL licenses.
 *
 * based on
 *
 * jQuery postMessage - v0.5 - 9/11/2009
 * http://benalman.com/projects/jquery-postmessage-plugin/
 * Copyright (c) 2009 "Cowboy" Ben Alman
 * Dual licensed under the MIT and GPL licenses.
 * http://benalman.com/about/license/
 */

// Release History
// visit https://github.com/LeoDutra/simple-postmessage/releases
//
//
// ender postMessage
// 0.1.3 - (5/1/2012) compatible with browserify
// 0.1.2 - (5/26/2011) Initial Fork and Release
//
// jQuery postMessage
// 0.5 - (9/11/2009) Improved cache-busting
// 0.4 - (8/25/2009) Initial release

!function (window) {
     // A few vars used in non-awesome browsers
     var interval_id,
	  last_hash,
	  original_hash,
	  cache_bust = 0,

	  // A var used in awesome browsers.
	  rm_callback,

	  // A few convenient shortcuts.
	  FALSE = !1,

	  // Reused internal strings.
	  postMessage = 'postMessage',
	  addEventListener = 'addEventListener',

	  // feature detection
      has_postMessage = window[postMessage],
      JSON = window.JSON,

      // ie 10- version detection. Useful to fix IE9 and IE8 problem when passing objects as message
      //   http://stackoverflow.com/a/15983064/1260526
      ua = navigator.userAgent.toLowerCase(),
      IE_VERSION = ~ua.indexOf('msie') && parseInt(ua.split('msie')[1], 10); // "~"" -> "x !== -1"

	  // Method: window.postMessage
	  //
	  // This method will call window.postMessage if available, setting the
	  // targetOrigin parameter to the base of the target_url parameter for maximum
	  // security in browsers that support it. If window.postMessage is not available,
	  // the target window's location.hash will be used to pass the message.
	  //
	  // Please Note: This version does not support the jQuery object serialization
	  // for postMessage
	  //
	  // Usage:
	  //
	  // > window.postMessage( message, target_url [, target ] );
	  //
	  // Arguments:
	  //
	  //  message - (String) A message to be passed to the other frame.
	  //  target_url - (String) The URL of the other frame this window is
	  //    attempting to communicate with. This must be the exact URL (including
	  //    any query string) of the other window for this script to work in
	  //    browsers that don't support window.postMessage.
	  //  target - (Object) A reference to the other frame this window is
	  //    attempting to communicate with. If omitted, defaults to `parent`.
	  //
	  // Returns:
	  //
	  //  Nothing.

	  window.simplePostMessage = function( message, target_url, target ) {
		if (!target_url) return;

		// Try to default to opener, parent if unspecified or cross-domain
		//   https://developer.mozilla.org/en-US/docs/Web/API/Window.opener
		//   https://developer.mozilla.org/en-US/docs/Web/API/Window.parent
		target = target || opener || parent;

		// The browser supports window.postMessage, so call it with a targetOrigin
		// set appropriately, based on the target_url parameter.
		if ( has_postMessage ) {

		  // IE9 and IE8 postMessage cannot send JS objects as message. Stringify is applied
		  if ( IE_VERSION > 10 && JSON /*IE8 compatibility mode has no JSON*/ ) {
		  	message = JSON.stringify(message);
		  }

		  target[postMessage]( message, target_url.replace( /([^:]+:\/\/[^\/]+).*/, '$1' ) );
		}

		  // The browser does not support window.postMessage, so set the location
		  // of the target to target_url#message. A bit ugly, but it works! A cache
		  // bust parameter is added to ensure that repeat messages trigger the
		  // callback.
		else
		  // encodeURIComponent prevents errors when sending URLs and invalid URI characters
		  target.location = target_url.replace( /#.*$/, '' ) + '#' + (+new Date) + (++cache_bust) + '&' + encodeURIComponent(message);
	  };

	  // Method: window.receiveMessage
	  //
	  // Register a single callback for either a window.postMessage call, if
	  // supported, or if unsupported, for any change in the current window
	  // location.hash. If window.postMessage is supported and source_origin is
	  // specified, the source window will be checked against this for maximum
	  // security. If window.postMessage is unsupported, a polling loop will be
	  // started to watch for changes to the location.hash.
	  //
	  // Note that for simplicity's sake, only a single callback can be registered
	  // at one time. Passing no params will unbind this event (or stop the polling
	  // loop), and calling this method a second time with another callback will
	  // unbind the event (or stop the polling loop) first, before binding the new
	  // callback.
	  //
	  // Also note that if window.postMessage is available, the optional
	  // source_origin param will be used to test the event.origin property. From
	  // the MDC window.postMessage docs: This string is the concatenation of the
	  // protocol and "://", the host name if one exists, and ":" followed by a port
	  // number if a port is present and differs from the default port for the given
	  // protocol. Examples of typical origins are https://example.org (implying
	  // port 443), http://example.net (implying port 80), and http://example.com:8080.
	  //
	  // Usage:
	  //
	  // > window.receiveMessage( callback [, source_origin ] [, delay ] );
	  //
	  // Arguments:
	  //
	  //  callback - (Function) This callback will execute whenever a <window.postMessage>
	  //    message is received, provided the source_origin matches. If callback is
	  //    omitted, any existing receiveMessage event bind or polling loop will be
	  //    canceled.
	  //  source_origin - (String) If window.postMessage is available and this value
	  //    is not equal to the event.origin property, the callback will not be
	  //    called.
	  //  source_origin - (Function) If window.postMessage is available and this
	  //    function returns false when passed the event.origin property, the
	  //    callback will not be called.
	  //  delay - (Number) An optional zero-or-greater delay in milliseconds at
	  //    which the polling loop will execute (for browser that don't support
	  //    window.postMessage). If omitted, defaults to 100.
	  //
	  // Returns:
	  //
	  //  Nothing!

	  window.simpleReceiveMessage = function( callback, source_origin, delay ) {
	  	var source_origin_type = typeof source_origin;
		if ( has_postMessage ) {
		  // Since the browser supports window.postMessage, the callback will be
		  // bound to the actual event associated with window.postMessage.

		  if ( callback ) {
			// Unbind an existing callback if it exists.
			rm_callback && window.simpleReceiveMessage();

			// Bind the callback. A reference to the callback is stored for ease of
			// unbinding.
			rm_callback = function(e) {
			  if ( ( source_origin_type === 'string' && e.origin !== source_origin )
				|| ( source_origin_type === 'function' && source_origin( e.origin ) === FALSE ) ) {
				return FALSE;
			  }
			  // IE9 and IE8 postMessage cannot send JS objects as message. Stringify is applied
			  if ( IE_VERSION > 10 && JSON /*IE8 compatibility mode has no JSON*/ ) {
			    e = JSON.parse(e);
			  }
			  callback( e );
			};
		  }

		  if ( window[addEventListener] ) {
			window[ callback ? addEventListener : 'removeEventListener' ]( 'message', rm_callback, FALSE );
		  } else {
			window[ callback ? 'attachEvent' : 'detachEvent' ]( 'onmessage', rm_callback );
		  }

		} else {
		  // Since the browser sucks, a polling loop will be started, and the
		  // callback will be called whenever the location.hash changes.

		  if ( interval_id ) {
		    interval_id = clearInterval( interval_id );
		  }

		  if ( callback ) {
			delay = source_origin_type === 'number'
			  ? source_origin
			  : typeof delay === 'number'
				? delay
				: 100;

			original_hash = document.location.hash;

			interval_id = setInterval(function(){
			  var hash = document.location.hash,
				re = /^#?\d+&/;
			  if ( hash !== last_hash && hash !== original_hash && re.test( hash ) ) {
				last_hash = hash;
				document.location.hash = original_hash ? original_hash : '';
				// replace(/\+/gim, ' ') fixes a Mozilla bug
				//   http://stackoverflow.com/questions/75980/best-practice-escape-or-encodeuri-encodeuricomponent/12796866#comment30658935_12796866
				callback({ data: decodeURIComponent(hash.replace( re, '' ).replace(/\+/gim, ' ')) });
			  }
			}, delay );
		  }
		}
	  };

}(window);
<commit_msg>Uses JSON serialization for IE7 and IE6 if window.JSON is present.<commit_after>/* simple postMessage
 * by Leonardo Dutra (https://github.com/LeoDutra)
 * Dual licensed under the MIT and GPL licenses.
 *
 * based on
 *
 * ender postMessage - v0.1.3 - 5/1/2012
 * by Thomas Sturm http://www.sturm.to
 * Dual licensed under the MIT and GPL licenses.
 *
 * based on
 *
 * jQuery postMessage - v0.5 - 9/11/2009
 * http://benalman.com/projects/jquery-postmessage-plugin/
 * Copyright (c) 2009 "Cowboy" Ben Alman
 * Dual licensed under the MIT and GPL licenses.
 * http://benalman.com/about/license/
 */

// Release History
// visit https://github.com/LeoDutra/simple-postmessage/releases
//
//
// ender postMessage
// 0.1.3 - (5/1/2012) compatible with browserify
// 0.1.2 - (5/26/2011) Initial Fork and Release
//
// jQuery postMessage
// 0.5 - (9/11/2009) Improved cache-busting
// 0.4 - (8/25/2009) Initial release

!function (window) {
     // A few vars used in non-awesome browsers
     var interval_id,
	  last_hash,
	  original_hash,
	  cache_bust = 0,

	  // A var used in awesome browsers.
	  rm_callback,

	  // A few convenient shortcuts.
	  FALSE = !1,

	  // Reused internal strings.
	  postMessage = 'postMessage',
	  addEventListener = 'addEventListener',

	  // feature detection
      has_postMessage = window[postMessage],
      JSON = window.JSON,

      // ie 10- version detection. Useful to fix IE9 and IE8 problem when passing objects as message
      //   http://stackoverflow.com/a/15983064/1260526
      ua = navigator.userAgent.toLowerCase(),
      IE_VERSION = ~ua.indexOf('msie') && parseInt(ua.split('msie')[1], 10); // "~"" -> "x !== -1"

	  // Method: window.postMessage
	  //
	  // This method will call window.postMessage if available, setting the
	  // targetOrigin parameter to the base of the target_url parameter for maximum
	  // security in browsers that support it. If window.postMessage is not available,
	  // the target window's location.hash will be used to pass the message.
	  //
	  // Please Note: This version does not support the jQuery object serialization
	  // for postMessage
	  //
	  // Usage:
	  //
	  // > window.postMessage( message, target_url [, target ] );
	  //
	  // Arguments:
	  //
	  //  message - (String) A message to be passed to the other frame.
	  //  target_url - (String) The URL of the other frame this window is
	  //    attempting to communicate with. This must be the exact URL (including
	  //    any query string) of the other window for this script to work in
	  //    browsers that don't support window.postMessage.
	  //  target - (Object) A reference to the other frame this window is
	  //    attempting to communicate with. If omitted, defaults to `parent`.
	  //
	  // Returns:
	  //
	  //  Nothing.

	  window.simplePostMessage = function( message, target_url, target ) {
		if (!target_url) return;

		// Try to default to opener, parent if unspecified or cross-domain
		//   https://developer.mozilla.org/en-US/docs/Web/API/Window.opener
		//   https://developer.mozilla.org/en-US/docs/Web/API/Window.parent
		target = target || opener || parent;

		// IE9 and IE8 postMessage cannot send JS objects as message. Stringify is applied
		// works for IE7, IE6 if Crockford's json.js is present
		if ( IE_VERSION > 10 && JSON /*IE8 compatibility mode has no JSON*/ ) {
			message = JSON.stringify(message);
		}

		// The browser supports window.postMessage, so call it with a targetOrigin
		// set appropriately, based on the target_url parameter.
		if ( has_postMessage ) {

		  target[postMessage]( message, target_url.replace( /([^:]+:\/\/[^\/]+).*/, '$1' ) );
		}

		  // The browser does not support window.postMessage, so set the location
		  // of the target to target_url#message. A bit ugly, but it works! A cache
		  // bust parameter is added to ensure that repeat messages trigger the
		  // callback.
		else
		  // encodeURIComponent prevents errors when sending URLs and invalid URI characters
		  target.location = target_url.replace( /#.*$/, '' ) + '#' + (+new Date) + (++cache_bust) + '&' + encodeURIComponent(message);
	  };

	  // Method: window.receiveMessage
	  //
	  // Register a single callback for either a window.postMessage call, if
	  // supported, or if unsupported, for any change in the current window
	  // location.hash. If window.postMessage is supported and source_origin is
	  // specified, the source window will be checked against this for maximum
	  // security. If window.postMessage is unsupported, a polling loop will be
	  // started to watch for changes to the location.hash.
	  //
	  // Note that for simplicity's sake, only a single callback can be registered
	  // at one time. Passing no params will unbind this event (or stop the polling
	  // loop), and calling this method a second time with another callback will
	  // unbind the event (or stop the polling loop) first, before binding the new
	  // callback.
	  //
	  // Also note that if window.postMessage is available, the optional
	  // source_origin param will be used to test the event.origin property. From
	  // the MDC window.postMessage docs: This string is the concatenation of the
	  // protocol and "://", the host name if one exists, and ":" followed by a port
	  // number if a port is present and differs from the default port for the given
	  // protocol. Examples of typical origins are https://example.org (implying
	  // port 443), http://example.net (implying port 80), and http://example.com:8080.
	  //
	  // Usage:
	  //
	  // > window.receiveMessage( callback [, source_origin ] [, delay ] );
	  //
	  // Arguments:
	  //
	  //  callback - (Function) This callback will execute whenever a <window.postMessage>
	  //    message is received, provided the source_origin matches. If callback is
	  //    omitted, any existing receiveMessage event bind or polling loop will be
	  //    canceled.
	  //  source_origin - (String) If window.postMessage is available and this value
	  //    is not equal to the event.origin property, the callback will not be
	  //    called.
	  //  source_origin - (Function) If window.postMessage is available and this
	  //    function returns false when passed the event.origin property, the
	  //    callback will not be called.
	  //  delay - (Number) An optional zero-or-greater delay in milliseconds at
	  //    which the polling loop will execute (for browser that don't support
	  //    window.postMessage). If omitted, defaults to 100.
	  //
	  // Returns:
	  //
	  //  Nothing!

	  window.simpleReceiveMessage = function( callback, source_origin, delay ) {
	  	var source_origin_type = typeof source_origin;
		if ( has_postMessage ) {
		  // Since the browser supports window.postMessage, the callback will be
		  // bound to the actual event associated with window.postMessage.

		  if ( callback ) {
			// Unbind an existing callback if it exists.
			rm_callback && window.simpleReceiveMessage();

			// Bind the callback. A reference to the callback is stored for ease of
			// unbinding.
			rm_callback = function(e) {
			  if ( ( source_origin_type === 'string' && e.origin !== source_origin )
				|| ( source_origin_type === 'function' && source_origin( e.origin ) === FALSE ) ) {
				return FALSE;
			  }
			  // IE9 and IE8 postMessage cannot send JS objects as message. Stringify is applied
			  // works for IE7, IE6 if Crockford's json.js is present
			  if ( IE_VERSION > 10 && JSON /*IE8 compatibility mode has no JSON*/ ) {
			    e = JSON.parse(e);
			  }
			  callback( e );
			};
		  }

		  if ( window[addEventListener] ) {
			window[ callback ? addEventListener : 'removeEventListener' ]( 'message', rm_callback, FALSE );
		  } else {
			window[ callback ? 'attachEvent' : 'detachEvent' ]( 'onmessage', rm_callback );
		  }

		} else {
		  // Since the browser sucks, a polling loop will be started, and the
		  // callback will be called whenever the location.hash changes.

		  if ( interval_id ) {
		    interval_id = clearInterval( interval_id );
		  }

		  if ( callback ) {
			delay = source_origin_type === 'number'
			  ? source_origin
			  : typeof delay === 'number'
				? delay
				: 100;

			original_hash = document.location.hash;

			interval_id = setInterval(function(){
			  var hash = document.location.hash,
				re = /^#?\d+&/;
			  if ( hash !== last_hash && hash !== original_hash && re.test( hash ) ) {
				last_hash = hash;
				document.location.hash = original_hash ? original_hash : '';
				// replace(/\+/gim, ' ') fixes a Mozilla bug
				//   http://stackoverflow.com/questions/75980/best-practice-escape-or-encodeuri-encodeuricomponent/12796866#comment30658935_12796866
				var data = decodeURIComponent(hash.replace( re, '' ).replace(/\+/gim, ' '));
				callback({
					data: JSON ? JSON.parse(data) : data
				});
			  }
			}, delay );
		  }
		}
	  };

}(window);
<|endoftext|>


<commit_before>/**
 * Music player in browser
 * author: kubosho_ <ta2@o2p.jp>
 * Repository: https://github.com/kubosho/browser-music-player
 * License: MIT
 */

(function (global, undefined) {
    'use strict';

    /**
     * @constructor
     */
    function MusicPlayer () {
        var AudioContext = global.AudioContext || global.webkitAudioContext;
        this.ctx = new AudioContext();
    }

    MusicPlayer.prototype.loadMusic = function (file, callback) {
        callback = callback || function(){};

        var that = this;
        var reader = new FileReader();

        reader.readAsDataURL(file);
        reader.onload = function (event) {
            if (!that.isMusicFile(file)) {
                return console.warn('Warning: Not a music file');
            }
            callback(event, reader.result);
        };

        return reader;
    };

    MusicPlayer.prototype.isMusicFile = function (file) {
        var type = file.type.substring(6, 9);
        return /(mp3|wav|ogg|wma|m4a)/.test(type);
    };

    MusicPlayer.prototype.createAudioElement = function (src) {
        var audio = document.createElement('audio');
        audio.src = src;
        return audio;
    };

    MusicPlayer.prototype.createSource = function (audioE) {
        return this.ctx.createMediaElementSource(audioE);
    };

    MusicPlayer.prototype.play = function (source) {
        source.connect(this.ctx.destination);
        source.start();
    };

    MusicPlayer.prototype.stop = function (source) {
        source.stop();
    };

    MusicPlayer.prototype.rewind = function () {
    };

    MusicPlayer.prototype.forward = function () {
    };

    global.MusicPlayer = MusicPlayer;
})(this.self, void 0);
<commit_msg>Delete a event arguments of callback()<commit_after>/**
 * Music player in browser
 * author: kubosho_ <ta2@o2p.jp>
 * Repository: https://github.com/kubosho/browser-music-player
 * License: MIT
 */

(function (global, undefined) {
    'use strict';

    /**
     * @constructor
     */
    function MusicPlayer () {
        var AudioContext = global.AudioContext || global.webkitAudioContext;
        this.ctx = new AudioContext();
    }

    MusicPlayer.prototype.loadMusic = function (file, callback) {
        callback = callback || function(){};

        var that = this;
        var reader = new FileReader();

        reader.readAsDataURL(file);
        reader.onload = function () {
            if (!that.isMusicFile(file)) {
                return console.warn('Warning: Not a music file');
            }

            callback(reader.result);
        };

        return reader;
    };

    MusicPlayer.prototype.isMusicFile = function (file) {
        var type = file.type.substring(6, 9);
        return /(mp3|wav|ogg|wma|m4a)/.test(type);
    };

    MusicPlayer.prototype.createAudioElement = function (src) {
        var audio = document.createElement('audio');
        audio.src = src;
        return audio;
    };

    MusicPlayer.prototype.createSource = function (audioE) {
        return this.ctx.createMediaElementSource(audioE);
    };

    MusicPlayer.prototype.play = function (source) {
        source.connect(this.ctx.destination);
        source.start();
    };

    MusicPlayer.prototype.stop = function (source) {
        source.stop();
    };

    MusicPlayer.prototype.rewind = function () {
    };

    MusicPlayer.prototype.forward = function () {
    };

    global.MusicPlayer = MusicPlayer;
})(this.self, void 0);
<|endoftext|>


<commit_before>var signals = require('signals');

/**
 * Manages render timing, pause and unpause
 * @param {View} view the view to manage
 */
function RenderManager(view) {

	this.view = view;
	this.skipFrames = 0;
	this.skipFramesCounter = 0;
	this.onEnterFrame = new signals.Signal();
	this.onExitFrame = new signals.Signal();
	this.renderLoop = this.renderLoop.bind(this);
};

RenderManager.prototype = {
	/**
	 * a flag to request that the render loop stops next at the next frame
	 * @type {Boolean}
	 */
	_requestStop: false,

	/**
	 * the repeating renderLoop calls itself with requestAnimationFrame to act as the render timer
	 */
	renderLoop : function() {
		if(this.skipFramesCounter < this.skipFrames) {
			this.skipFramesCounter++;
		} else {
			this.onEnterFrame.dispatch();
			this.view.render();
			this.onExitFrame.dispatch();
			this.skipFramesCounter = 0;
		}
		if(!this._requestStop) requestAnimationFrame(this.renderLoop);
	},

	/**
	 * start rendering
	 */
	start: function() {
		if(this._requestStop = false) return;
		this._requestStop = false;
		requestAnimationFrame(this.renderLoop);
	},

	/**
	 * stop rendering
	 */
	stop: function() {
		this._requestStop = true;
	}
}

module.exports = RenderManager;<commit_msg>render individual frames if you want without loop<commit_after>var signals = require('signals');

/**
 * Manages render timing, pause and unpause
 * @param {View} view the view to manage
 */
function RenderManager(view) {

	this.view = view;
	this.skipFrames = 0;
	this.skipFramesCounter = 0;
	this.onEnterFrame = new signals.Signal();
	this.onExitFrame = new signals.Signal();
	this.render = this.render.bind(this);
	this.renderLoop = this.renderLoop.bind(this);
};

RenderManager.prototype = {
	/**
	 * a flag to request that the render loop stops next at the next frame
	 * @type {Boolean}
	 */
	_requestStop: false,

	/**
	 * the repeating renderLoop calls itself with requestAnimationFrame to act as the render timer
	 */
	renderLoop : function() {
		if(this.skipFramesCounter < this.skipFrames) {
			this.skipFramesCounter++;
		} else {
			this.render();
			this.skipFramesCounter = 0;
		}
		if(!this._requestStop) requestAnimationFrame(this.renderLoop);
	},

	/**
	 * render one frame
	 */
	render: function() {
		this.onEnterFrame.dispatch();
		this.view.render();
		this.onExitFrame.dispatch();
	},

	/**
	 * start rendering
	 */
	start: function() {
		if(this._requestStop = false) return;
		this._requestStop = false;
		requestAnimationFrame(this.renderLoop);
	},

	/**
	 * stop rendering
	 */
	stop: function() {
		this._requestStop = true;
	}
}

module.exports = RenderManager;<|endoftext|>


<commit_before>"use strict";

var _ = require('lodash'),
    Bacon = require('baconjs'),
    url = require('url'),
    L = require('leaflet'),
    History = require('history'),

    modeNamesForUrl = [
        require('treemap/addTreeMode').name,
        require('treemap/addResourceMode').name,
        'prioritization',
        'scenarios'
    ];

var _state = null,
    _stateChangeBus = new Bacon.Bus(),
    _history = null,
    _window = null;

function HistoryApi() {
    function onStateChange(callback) {
        History.Adapter.bind(window, 'statechange', callback);
        // We use the "History" library for "pushState" etc. capabilities on IE9.
        // If we drop IE9 support we should change "History" to "history" in this
        // module, and replace the above "bind" call with:
        //        window.onpopstate = function(event) {
        //            setStateAndPushToApp(event.state || getStateFromCurrentUrl());
        //        };
    }
    function getState() {
        return History.getState();
    }
    function pushState(state, title, url) {
        History.pushState(state, title, url);
    }
    function replaceState(state, title, url) {
        History.replaceState(state, title, url);
    }
    return {
        onStateChange: onStateChange,
        getState: getState,
        pushState: pushState,
        replaceState: replaceState
    };
}

function WindowApi() {
    return {
        getLocationHref: function() {
            return window.location.href;
        }
    };
}

// Serialize state to query.
var serializers = {
    zoomLatLng: function(state, query) {
        if (state.zoomLatLng) {
            var zoom = state.zoomLatLng.zoom,
                precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2)),
                lat = state.zoomLatLng.lat.toFixed(precision),
                lng = state.zoomLatLng.lng.toFixed(precision);
            query.z = [zoom, lat, lng].join('/');
        }
    },

    search: function(state, query) {
        if (state.search && state.search.filter && !_.isEmpty(state.search.filter)) {
            query.q = JSON.stringify(state.search.filter);
        }
        if (state.search && state.search.display) {
            query.show = JSON.stringify(state.search.display);
        }
    },

    modeName: function(state, query) {
        if (state.modeName) {
            query.m = state.modeName;
        }
    }
};

// Deserialize query to newState.
var deserializers = {
    z: function(newState, query) {
        var zoomLatLng = query.z;
        newState.zoomLatLng = null;
        if (zoomLatLng) {
            var parts = zoomLatLng.split("/"),
                zoom = parseInt(parts[0], 10),
                lat = parseFloat(parts[1]),
                lng = parseFloat(parts[2]);
            if (!isNaN(zoom) && !isNaN(lat) && !isNaN(lng)) {
                newState.zoomLatLng = makeZoomLatLng(zoom, lat, lng);
            }
        }
    },

    q: function(newState, query) {
        newState.search = newState.search || {};
        newState.search.filter = JSON.parse(query.q || '{}');
    },

    show: function(newState, query) {
        newState.search = newState.search || {};
        newState.search.display = query.show ? JSON.parse(query.show) : undefined;
    },

    m: function(newState, query) {
        newState.modeName = query.m || '';
    }
};

function set(key, value, options) {
    options = _.defaults({}, options, {
        silent: false,
        replaceState: false
    });
    var currentValue = _state && _state[key];
    if (!_.isEqual(currentValue, value)) {
        var newState = _.extend({}, _state);
        newState[key] = value;

        if (options.silent) {
            _state[key] = value;
        }

        if (options.replaceState) {
            _history.replaceState(newState, document.title, getUrlFromState(newState));
        } else {
            _history.pushState(newState, document.title, getUrlFromState(newState));
        }
    }
}

module.exports = {
    init: function (options) {
        options = _.defaults({}, options, {
            historyApi: new HistoryApi(),
            windowApi: new WindowApi()
        });

        _state = null;
        _history = options.historyApi;
        _window = options.windowApi;

        setStateAndPushToApp(getStateFromCurrentUrl());

        _history.onStateChange(function() {
            setStateAndPushToApp(_history.getState().data || getStateFromCurrentUrl());
        });
    },

    setZoomLatLng: function (zoom, center) {
        var zoomLatLng = makeZoomLatLng(zoom, center.lat, center.lng);
        if (!_.isEqual(zoomLatLng, _state.zoomLatLng)) {
            _state.zoomLatLng = zoomLatLng;
            _history.replaceState(_state, document.title, getUrlFromCurrentState());
        }
    },

    setSearch: function (otmSearch) {
        if (!_.isEqual(otmSearch, _state.search)) {
            _state.search = otmSearch;
            _history.pushState(_state, document.title, getUrlFromCurrentState());
        }
    },

    setModeName: function (modeName) {
        modeName = _.contains(modeNamesForUrl, modeName) ? modeName : '';
        if (modeName !== _state.modeName) {
            _state.modeName = modeName;
            _history.replaceState(_state, document.title, getUrlFromCurrentState());
        }
    },

    getSearch: function() {
        return _state.search;
    },

    get: function(key) {
        return _state[key];
    },

    set: set,

    // TODO: Rename to changeStream.
    stateChangeStream: _stateChangeBus.map(_.identity)
};

function getStateFromCurrentUrl() {
    var newState = {},
        query = url.parse(_window.getLocationHref(), true).query,
        allKeys = _.union(_.keys(deserializers), _.keys(query));

    _.each(allKeys, function(k) {
        var deserialize = deserializers[k];
        if (deserialize) {
            deserialize(newState, query);
        } else {
            newState[k] = query[k];
        }
    });

    return newState;
}

function makeZoomLatLng(zoom, lat, lng) {
    return { zoom: zoom, lat: lat, lng: lng };
}

function getUrlFromState(state) {
    var parsedUrl = url.parse(_window.getLocationHref()),
        query = {};

    _.each(state, function(v, k) {
        var serialize = serializers[k];
        if (serialize) {
            serialize(state, query);
        } else {
            query[k] = v;
        }
    });

    parsedUrl.query = query;
    parsedUrl.search = null;
    var urlText = url.format(parsedUrl).replace(/%2F/g, '/');
    return urlText;
}

function getUrlFromCurrentState() {
    return getUrlFromState(_state);
}

function setStateAndPushToApp(newState) {
    var changedState = _.omit(newState, function(v, k) {
        return _state && _.isEqual(_state[k], v);
    });
    _state = newState;
    if (!_.isEmpty(changedState)) {
        _stateChangeBus.push(changedState);
    }
}
<commit_msg>Centralize all urlState setters<commit_after>"use strict";

var _ = require('lodash'),
    Bacon = require('baconjs'),
    url = require('url'),
    L = require('leaflet'),
    History = require('history'),

    modeNamesForUrl = [
        require('treemap/addTreeMode').name,
        require('treemap/addResourceMode').name,
        'prioritization',
        'scenarios'
    ];

var _state = null,
    _stateChangeBus = new Bacon.Bus(),
    _history = null,
    _window = null;

function HistoryApi() {
    function onStateChange(callback) {
        History.Adapter.bind(window, 'statechange', callback);
        // We use the "History" library for "pushState" etc. capabilities on IE9.
        // If we drop IE9 support we should change "History" to "history" in this
        // module, and replace the above "bind" call with:
        //        window.onpopstate = function(event) {
        //            setStateAndPushToApp(event.state || getStateFromCurrentUrl());
        //        };
    }
    function getState() {
        return History.getState();
    }
    function pushState(state, title, url) {
        History.pushState(state, title, url);
    }
    function replaceState(state, title, url) {
        History.replaceState(state, title, url);
    }
    return {
        onStateChange: onStateChange,
        getState: getState,
        pushState: pushState,
        replaceState: replaceState
    };
}

function WindowApi() {
    return {
        getLocationHref: function() {
            return window.location.href;
        }
    };
}

// Serialize state to query.
var serializers = {
    zoomLatLng: function(state, query) {
        if (state.zoomLatLng) {
            var zoom = state.zoomLatLng.zoom,
                precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2)),
                lat = state.zoomLatLng.lat.toFixed(precision),
                lng = state.zoomLatLng.lng.toFixed(precision);
            query.z = [zoom, lat, lng].join('/');
        }
    },

    search: function(state, query) {
        if (state.search && state.search.filter && !_.isEmpty(state.search.filter)) {
            query.q = JSON.stringify(state.search.filter);
        }
        if (state.search && state.search.display) {
            query.show = JSON.stringify(state.search.display);
        }
    },

    modeName: function(state, query) {
        if (state.modeName) {
            query.m = state.modeName;
        }
    }
};

// Deserialize query to newState.
var deserializers = {
    z: function(newState, query) {
        var zoomLatLng = query.z;
        newState.zoomLatLng = null;
        if (zoomLatLng) {
            var parts = zoomLatLng.split("/"),
                zoom = parseInt(parts[0], 10),
                lat = parseFloat(parts[1]),
                lng = parseFloat(parts[2]);
            if (!isNaN(zoom) && !isNaN(lat) && !isNaN(lng)) {
                newState.zoomLatLng = makeZoomLatLng(zoom, lat, lng);
            }
        }
    },

    q: function(newState, query) {
        newState.search = newState.search || {};
        newState.search.filter = JSON.parse(query.q || '{}');
    },

    show: function(newState, query) {
        newState.search = newState.search || {};
        newState.search.display = query.show ? JSON.parse(query.show) : undefined;
    },

    m: function(newState, query) {
        newState.modeName = query.m || '';
    }
};

function set(key, value, options) {
    options = _.defaults({}, options, {
        silent: false,
        replaceState: false
    });

    var currentValue = _state && _state[key];

    if (!_.isEqual(currentValue, value)) {
        var newState = _.extend({}, _state);
        newState[key] = value;

        // If there are no differences between _state and newState then there
        // will be nothing for stateChangeStream to emit.
        if (options.silent) {
            _state[key] = value;
        }

        if (options.replaceState) {
            _history.replaceState(newState, document.title, getUrlFromState(newState));
        } else {
            _history.pushState(newState, document.title, getUrlFromState(newState));
        }
    }
}

module.exports = {
    init: function (options) {
        options = _.defaults({}, options, {
            historyApi: new HistoryApi(),
            windowApi: new WindowApi()
        });

        _state = null;
        _history = options.historyApi;
        _window = options.windowApi;

        setStateAndPushToApp(getStateFromCurrentUrl());

        _history.onStateChange(function() {
            setStateAndPushToApp(_history.getState().data || getStateFromCurrentUrl());
        });
    },

    setZoomLatLng: function (zoom, center) {
        var zoomLatLng = makeZoomLatLng(zoom, center.lat, center.lng);
        set('zoomLatLng', zoomLatLng, {
            silent: true,
            replaceState: true
        });
    },

    setSearch: function (otmSearch) {
        set('search', otmSearch, {
            silent: true,
            replaceState: false
        });
    },

    setModeName: function (modeName) {
        modeName = _.contains(modeNamesForUrl, modeName) ? modeName : '';
        set('modeName', modeName, {
            silent: true,
            replaceState: true
        });
    },

    getSearch: function() {
        return _state.search;
    },

    get: function(key) {
        return _state[key];
    },

    set: set,

    // TODO: Rename to changeStream.
    stateChangeStream: _stateChangeBus.map(_.identity)
};

function getStateFromCurrentUrl() {
    var newState = {},
        query = url.parse(_window.getLocationHref(), true).query,
        allKeys = _.union(_.keys(deserializers), _.keys(query));

    _.each(allKeys, function(k) {
        var deserialize = deserializers[k];
        if (deserialize) {
            deserialize(newState, query);
        } else {
            newState[k] = query[k];
        }
    });

    return newState;
}

function makeZoomLatLng(zoom, lat, lng) {
    return { zoom: zoom, lat: lat, lng: lng };
}

function getUrlFromState(state) {
    var parsedUrl = url.parse(_window.getLocationHref()),
        query = {};

    _.each(state, function(v, k) {
        var serialize = serializers[k];
        if (serialize) {
            serialize(state, query);
        } else {
            query[k] = v;
        }
    });

    parsedUrl.query = query;
    parsedUrl.search = null;
    var urlText = url.format(parsedUrl).replace(/%2F/g, '/');
    return urlText;
}

function setStateAndPushToApp(newState) {
    var changedState = _.omit(newState, function(v, k) {
        return _state && _.isEqual(_state[k], v);
    });
    _state = newState;
    if (!_.isEmpty(changedState)) {
        _stateChangeBus.push(changedState);
    }
}
<|endoftext|>


<commit_before>(function ($) {
  // register namespace
  $.extend(true, window, {
    "Slick": {
      "Plugins": {
        "DistinctMenu": DistinctMenu
       }
    }
  });
  function DistinctMenu(options){
    var _grid;
    var _self = this;
    var _handler = new Slick.EventHandler();
    var _defaults = {
      command: 'filter',            //headerMenuItem command key
      headerMenu: false,            //headerMenuPlugin instance
      columns: [],                  //columns object which was used in headerMenuPlugin
      url: '/items',                //default url for query distinct arrays (JSON format)
      urlParameters: null,          //extra parameters for url string
      exclude: [],                  //exclude column id's
      doUrl: doUrl,                 //function that generate url.        f(field, condition)
      doCondition: doCondition,     //function that generate conditions. f(field, value){
      doFilter: doFilter,           //function that fetch data           f(field, condition)
      onReady: onReady,             //function when menus are ready      f(columns) -> grid.setColumns(columns); grid.render();
      selectIcon: "/js/vendor/SlickGrid/images/tick.png",
      condition: {$and: []}         //internal condition
    };

    function init(grid) {
      //Init plugin
      options = $.extend(true, {}, _defaults, options);
      _grid = grid;
      condition(false);
      options.headerMenu.onCommand.subscribe(onCommand);
      console.log('distinctMenu initialized');
    }

    // Condition handler
    function condition(field, condition, or){
      var obj = {}
      if( field === false ){
        options.condition['$and'] = [];
        for (var i=1;i<=options.columns.length;i++){
          options.condition['$and'].push({});
        }
      } else if(field && condition ) {
        var col = getColumnIndexByField(field);
        if( or ) {
          if( !options.condition['$and'][col]['$or'] ){
            var org = JSON.stringify(options.condition['$and'][col]);
            options.condition['$and'][col] = {};
            options.condition['$and'][col]['$or'] = [ JSON.parse(org) ];
          }
          options.condition['$and'][col]['$or'].push( condition );
        } else {
          options.condition['$and'][col] = condition;
        }
      }
      function cleanCondition(myObjects)
      {
        var and = {'$and': myObjects['$and'].filter(function (val) {
            return Object.keys(val).length!=0;
        })};
        if( JSON.stringify(and['$and'])=='{}') return {};
      }
      return cleanCondition(options.condition);
    }
    function getColumnIndexByField(field)
    {
      var i;
      for(i=0;i<options.columns.length;i++){
        if( options.columns[i].field == field ) return i;
      } return;
    }
    function onCommand(e, args) {

      if( args.command == options.command ){
        //options.exclude = args.column.field
        setSelection(args.column, args.item, e.ctrlKey);
        condition(args.column.field, args.item.condition, e.ctrlKey);
        options.doFilter( args.column.field, condition() );
      } else if( args.command == 'filter' ) {
        setSelection(args.column, args.item, e.ctrlKey);
        condition(args.column.field, args.item.condition, e.ctrlKey);
        options.doFilter( args.column.field, condition() );
      }
    }
    function destroy() {
      _handler.unsubscribeAll();
      console.log('distinctMenu destroyed');
    }
    function setSelection(column, item, or){
      $.extend(true, column, {header: {menu: {items: []}}})
      var items = column.header.menu.items;
      items.forEach( function( value ) {
        if( value.command == options.command ){
          //belong to distinct menus
          if( value.title === item.title ){
            value.iconImage = options.selectIcon;
          } else if(!or){
            value.iconImage = false;
          }
        }
      });
    }
    function onReady(columns){
      _grid.setColumns(columns);
      _grid.render();
      console.log('ready');
    }
    function doUrl(field, condition){
      if( typeof(condition)=='object'){
        condition = JSON.stringify(condition);
      }
      var url = options.url+'?t=distinct&f='+field;
      if( condition ) url +='&q='+condition;
      return url;
    }

    function doCondition(field, value){
      var cond = {}
      cond[field] = value;
      return cond;
    }

    var updateDistinct = function(items, i, callback){

      if( i < items.length ) {
        if( options.exclude.indexOf(items[i].id)>=0 || items[i].name[0]==='<' ) {
          updateDistinct(items, i+1, callback);
          return;
        }
        var url = false;
        if( typeof(options.doUrl)=='function'){
          url = options.doUrl(items[i].field, condition());
        }else {
          console.error('doUrl function missing!');
          //doError({error: 'doUrl function missing!');
          return;
        }
        $.getJSON( url, options.urlParameters
        ).done(function( json  ) {
            if( typeof(json) == 'object' ){
              console.log(json.length);
              setColumnMenuFilters(i, items[i], json);
            } else {
              console.error('invalid response format');
            }
            updateDistinct(items, i+1, callback);
        }).fail(function(jqxhr, textStatus, error) {
          var err = textStatus + ", " + error;
          console.error( "Request Failed: " + err );
          updateDistinct(items, i+1, callback);
        });
      } else {
        callback(options.columns);
      }
    }
    function updateMenus(){
      updateDistinct(options.columns, 0, options.onReady);
    }
    function doFilter(field, condition){
      console.log('Filteringing with condition: '+JSON.stringify(condition) );
    }
    function removeFunction (myObjects,prop,valu)
    {
      return myObjects.filter(function (val) {
          return val[prop] !== valu;
      });
    }
    function setColumnMenuFilters(coll, item, distinct){
      console.log('setColumnMenuFilters: '+distinct);
      $.extend(true, options.columns[coll], {header: {menu: {items: []}}})

      //remove old items (only where command is "distinct")
      var items = options.columns[coll].header.menu.items;
      options.columns[coll].header.menu.items = removeFunction(items, 'command', options.command);
      var items = options.columns[coll].header.menu.items;
      //push "all" button
      items.push( {
        command: options.command,
        title: 'All',
        condition: {},
        iconImage: options.selectIcon,
      });
      //push individual items
      distinct.forEach( function( value ) {
        console.log('addItem: '+value);
        items.push({
          command: options.command,
          title: value,
          condition: options.doCondition(item.field, value),
        });
      });

    }
    $.extend(this, {
      "init": init,
      "destroy": destroy,
      "update": updateMenus,
      //"onBeforeMenuShow": new Slick.Event(),
      //"onCommand": new Slick.Event()
    });
  }
})(jQuery);
<commit_msg>added condition public -function<commit_after>(function ($) {
  // register namespace
  $.extend(true, window, {
    "Slick": {
      "Plugins": {
        "DistinctMenu": DistinctMenu
       }
    }
  });
  function DistinctMenu(options){
    var _grid;
    var _self = this;
    var _handler = new Slick.EventHandler();
    var _defaults = {
      command: 'filter',            //headerMenuItem command key
      headerMenu: false,            //headerMenuPlugin instance
      columns: [],                  //columns object which was used in headerMenuPlugin
      url: '/items',                //default url for query distinct arrays (JSON format)
      urlParameters: null,          //extra parameters for url string
      exclude: [],                  //exclude column id's
      doUrl: doUrl,                 //function that generate url.        f(field, condition)
      doCondition: doCondition,     //function that generate conditions. f(field, value){
      doFilter: doFilter,           //function that fetch data           f(field, condition)
      onReady: onReady,             //function when menus are ready      f(columns) -> grid.setColumns(columns); grid.render();
      selectIcon: "/js/vendor/SlickGrid/images/tick.png",
      condition: {$and: []}         //internal condition
    };

    function init(grid) {
      //Init plugin
      options = $.extend(true, {}, _defaults, options);
      _grid = grid;
      condition(false);
      options.headerMenu.onCommand.subscribe(onCommand);
      console.log('distinctMenu initialized');
    }

    // Condition handler
    function condition(field, condition, or){
      var obj = {}
      if( field === false ){
        options.condition['$and'] = [];
        for (var i=1;i<=options.columns.length;i++){
          options.condition['$and'].push({});
        }
      } else if(field && condition ) {
        var col = getColumnIndexByField(field);
        if( or ) {
          if( !options.condition['$and'][col]['$or'] ){
            var org = JSON.stringify(options.condition['$and'][col]);
            options.condition['$and'][col] = {};
            options.condition['$and'][col]['$or'] = [ JSON.parse(org) ];
          }
          options.condition['$and'][col]['$or'].push( condition );
        } else {
          options.condition['$and'][col] = condition;
        }
      }
      function cleanCondition(myObjects)
      {
        var and = {'$and': myObjects['$and'].filter(function (val) {
            return Object.keys(val).length!=0;
        })};
        if( JSON.stringify(and['$and'])=='{}') return {};
      }
      return cleanCondition(options.condition);
    }
    function getColumnIndexByField(field)
    {
      var i;
      for(i=0;i<options.columns.length;i++){
        if( options.columns[i].field == field ) return i;
      } return;
    }
    function onCommand(e, args) {

      if( args.command == options.command ){
        //options.exclude = args.column.field
        setSelection(args.column, args.item, e.ctrlKey);
        condition(args.column.field, args.item.condition, e.ctrlKey);
        options.doFilter( args.column.field, condition() );
      } else if( args.command == 'filter' ) {
        setSelection(args.column, args.item, e.ctrlKey);
        condition(args.column.field, args.item.condition, e.ctrlKey);
        options.doFilter( args.column.field, condition() );
      }
    }
    function destroy() {
      _handler.unsubscribeAll();
      console.log('distinctMenu destroyed');
    }
    function setSelection(column, item, or){
      $.extend(true, column, {header: {menu: {items: []}}})
      var items = column.header.menu.items;
      items.forEach( function( value ) {
        if( value.command == options.command ){
          //belong to distinct menus
          if( value.title === item.title ){
            value.iconImage = options.selectIcon;
          } else if(!or){
            value.iconImage = false;
          }
        }
      });
    }
    function onReady(columns){
      _grid.setColumns(columns);
      _grid.render();
      console.log('ready');
    }
    function doUrl(field, condition){
      if( typeof(condition)=='object'){
        condition = JSON.stringify(condition);
      }
      var url = options.url+'?t=distinct&f='+field;
      if( condition ) url +='&q='+condition;
      return url;
    }

    function doCondition(field, value){
      var cond = {}
      cond[field] = value;
      return cond;
    }

    var updateDistinct = function(items, i, callback){

      if( i < items.length ) {
        if( options.exclude.indexOf(items[i].id)>=0 || items[i].name[0]==='<' ) {
          updateDistinct(items, i+1, callback);
          return;
        }
        var url = false;
        if( typeof(options.doUrl)=='function'){
          url = options.doUrl(items[i].field, condition());
        }else {
          console.error('doUrl function missing!');
          //doError({error: 'doUrl function missing!');
          return;
        }
        $.getJSON( url, options.urlParameters
        ).done(function( json  ) {
            if( typeof(json) == 'object' ){
              console.log(json.length);
              setColumnMenuFilters(i, items[i], json);
            } else {
              console.error('invalid response format');
            }
            updateDistinct(items, i+1, callback);
        }).fail(function(jqxhr, textStatus, error) {
          var err = textStatus + ", " + error;
          console.error( "Request Failed: " + err );
          updateDistinct(items, i+1, callback);
        });
      } else {
        callback(options.columns);
      }
    }
    function updateMenus(){
      updateDistinct(options.columns, 0, options.onReady);
    }
    function doFilter(field, condition){
      console.log('Filteringing with condition: '+JSON.stringify(condition) );
    }
    function removeFunction (myObjects,prop,valu)
    {
      return myObjects.filter(function (val) {
          return val[prop] !== valu;
      });
    }
    function setColumnMenuFilters(coll, item, distinct){
      console.log('setColumnMenuFilters: '+distinct);
      $.extend(true, options.columns[coll], {header: {menu: {items: []}}})

      //remove old items (only where command is "distinct")
      var items = options.columns[coll].header.menu.items;
      options.columns[coll].header.menu.items = removeFunction(items, 'command', options.command);
      var items = options.columns[coll].header.menu.items;
      //push "all" button
      items.push( {
        command: options.command,
        title: 'All',
        condition: {},
        iconImage: options.selectIcon,
      });
      //push individual items
      distinct.forEach( function( value ) {
        console.log('addItem: '+value);
        items.push({
          command: options.command,
          title: value,
          condition: options.doCondition(item.field, value),
        });
      });

    }
    $.extend(this, {
      "init": init,
      "destroy": destroy,
      "update": updateMenus,
      "condition": condition,
      //"onBeforeMenuShow": new Slick.Event(),
      //"onCommand": new Slick.Event()
    });
  }
})(jQuery);
<|endoftext|>


<commit_before>/*
Copyright 2015, 2016 OpenMarket Ltd

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import MatrixClientPeg from "./MatrixClientPeg";
import dis from "./dispatcher";
import Tinter from "./Tinter";
import sdk from './index';
import { _t } from './languageHandler';
import Modal from './Modal';

class Command {
    constructor(name, paramArgs, runFn) {
        this.name = name;
        this.paramArgs = paramArgs;
        this.runFn = runFn;
    }

    getCommand() {
        return "/" + this.name;
    }

    getCommandWithArgs() {
        return this.getCommand() + " " + this.paramArgs;
    }

    run(roomId, args) {
        return this.runFn.bind(this)(roomId, args);
    }

    getUsage() {
        return _t('Usage') + ': ' + this.getCommandWithArgs();
    }
}

function reject(msg) {
    return {
        error: msg,
    };
}

function success(promise) {
    return {
        promise: promise,
    };
}

/* Disable the "unexpected this" error for these commands - all of the run
 * functions are called with `this` bound to the Command instance.
 */

/* eslint-disable babel/no-invalid-this */

const commands = {
    ddg: new Command("ddg", "<query>", function(roomId, args) {
        const ErrorDialog = sdk.getComponent('dialogs.ErrorDialog');
        // TODO Don't explain this away, actually show a search UI here.
        Modal.createTrackedDialog('Slash Commands', '/ddg is not a command', ErrorDialog, {
            title: _t('/ddg is not a command'),
            description: _t('To use it, just wait for autocomplete results to load and tab through them.'),
        });
        return success();
    }),

    // Change your nickname
    nick: new Command("nick", "<display_name>", function(roomId, args) {
        if (args) {
            return success(
                MatrixClientPeg.get().setDisplayName(args),
            );
        }
        return reject(this.getUsage());
    }),

    // Changes the colorscheme of your current room
    tint: new Command("tint", "<color1> [<color2>]", function(roomId, args) {
        if (args) {
            const matches = args.match(/^(#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}))( +(#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})))?$/);
            if (matches) {
                Tinter.tint(matches[1], matches[4]);
                const colorScheme = {};
                colorScheme.primary_color = matches[1];
                if (matches[4]) {
                    colorScheme.secondary_color = matches[4];
                }
                return success(
                    MatrixClientPeg.get().setRoomAccountData(
                        roomId, "org.matrix.room.color_scheme", colorScheme,
                    ),
                );
            }
        }
        return reject(this.getUsage());
    }),

    // Change the room topic
    topic: new Command("topic", "<topic>", function(roomId, args) {
        if (args) {
            return success(
                MatrixClientPeg.get().setRoomTopic(roomId, args),
            );
        }
        return reject(this.getUsage());
    }),

    // Invite a user
    invite: new Command("invite", "<userId>", function(roomId, args) {
        if (args) {
            const matches = args.match(/^(\S+)$/);
            if (matches) {
                return success(
                    MatrixClientPeg.get().invite(roomId, matches[1]),
                );
            }
        }
        return reject(this.getUsage());
    }),

    // Join a room
    join: new Command("join", "#alias:domain", function(roomId, args) {
        if (args) {
            const matches = args.match(/^(\S+)$/);
            if (matches) {
                let roomAlias = matches[1];
                if (roomAlias[0] !== '#') {
                    return reject(this.getUsage());
                }
                if (!roomAlias.match(/:/)) {
                    roomAlias += ':' + MatrixClientPeg.get().getDomain();
                }

                dis.dispatch({
                    action: 'view_room',
                    room_alias: roomAlias,
                    auto_join: true,
                });

                return success();
            }
        }
        return reject(this.getUsage());
    }),

    part: new Command("part", "[#alias:domain]", function(roomId, args) {
        let targetRoomId;
        if (args) {
            const matches = args.match(/^(\S+)$/);
            if (matches) {
                let roomAlias = matches[1];
                if (roomAlias[0] !== '#') {
                    return reject(this.getUsage());
                }
                if (!roomAlias.match(/:/)) {
                    roomAlias += ':' + MatrixClientPeg.get().getDomain();
                }

                // Try to find a room with this alias
                const rooms = MatrixClientPeg.get().getRooms();
                for (let i = 0; i < rooms.length; i++) {
                    const aliasEvents = rooms[i].currentState.getStateEvents(
                        "m.room.aliases",
                    );
                    for (let j = 0; j < aliasEvents.length; j++) {
                        const aliases = aliasEvents[j].getContent().aliases || [];
                        for (let k = 0; k < aliases.length; k++) {
                            if (aliases[k] === roomAlias) {
                                targetRoomId = rooms[i].roomId;
                                break;
                            }
                        }
                        if (targetRoomId) { break; }
                    }
                    if (targetRoomId) { break; }
                }
                if (!targetRoomId) {
                    return reject(_t("Unrecognised room alias:") + ' ' + roomAlias);
                }
            }
        }
        if (!targetRoomId) targetRoomId = roomId;
        return success(
            MatrixClientPeg.get().leave(targetRoomId).then(
                function() {
                    dis.dispatch({action: 'view_next_room'});
                },
            ),
        );
    }),

    // Kick a user from the room with an optional reason
    kick: new Command("kick", "<userId> [<reason>]", function(roomId, args) {
        if (args) {
            const matches = args.match(/^(\S+?)( +(.*))?$/);
            if (matches) {
                return success(
                    MatrixClientPeg.get().kick(roomId, matches[1], matches[3]),
                );
            }
        }
        return reject(this.getUsage());
    }),

    // Ban a user from the room with an optional reason
    ban: new Command("ban", "<userId> [<reason>]", function(roomId, args) {
        if (args) {
            const matches = args.match(/^(\S+?)( +(.*))?$/);
            if (matches) {
                return success(
                    MatrixClientPeg.get().ban(roomId, matches[1], matches[3]),
                );
            }
        }
        return reject(this.getUsage());
    }),

    // Unban a user from the room
    unban: new Command("unban", "<userId>", function(roomId, args) {
        if (args) {
            const matches = args.match(/^(\S+)$/);
            if (matches) {
                // Reset the user membership to "leave" to unban him
                return success(
                    MatrixClientPeg.get().unban(roomId, matches[1]),
                );
            }
        }
        return reject(this.getUsage());
    }),

    ignore: new Command("ignore", "<userId>", function(roomId, args) {
        if (args) {
            const matches = args.match(/^(\S+)$/);
            if (matches) {
                const userId = matches[1];
                const ignoredUsers = MatrixClientPeg.get().getIgnoredUsers();
                ignoredUsers.push(userId); // de-duped internally below
                return success(
                    MatrixClientPeg.get().setIgnoredUsers(ignoredUsers).then(() => {
                        const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
                        Modal.createTrackedDialog('Slash Commands', 'User ignored', QuestionDialog, {
                            title: _t("Ignored user"),
                            description: (

{_t("You are now ignoring %(userId)s", {userId: userId})}

                            ),
                            hasCancelButton: false,
                        });
                    }),
                );
            }
        }
        return reject(this.getUsage());
    }),

    unignore: new Command("unignore", "<userId>", function(roomId, args) {
        if (args) {
            const matches = args.match(/^(\S+)$/);
            if (matches) {
                const userId = matches[1];
                const ignoredUsers = MatrixClientPeg.get().getIgnoredUsers();
                const index = ignoredUsers.indexOf(userId);
                if (index !== -1) ignoredUsers.splice(index, 1);
                return success(
                    MatrixClientPeg.get().setIgnoredUsers(ignoredUsers).then(() => {
                        const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
                        Modal.createTrackedDialog('Slash Commands', 'User unignored', QuestionDialog, {
                            title: _t("Unignored user"),
                            description: (

{_t("You are no longer ignoring %(userId)s", {userId: userId})}

                            ),
                            hasCancelButton: false,
                        });
                    }),
                );
            }
        }
        return reject(this.getUsage());
    }),

    // Define the power level of a user
    op: new Command("op", "<userId> [<power level>]", function(roomId, args) {
        if (args) {
            const matches = args.match(/^(\S+?)( +(\d+))?$/);
            let powerLevel = 50; // default power level for op
            if (matches) {
                const userId = matches[1];
                if (matches.length === 4 && undefined !== matches[3]) {
                    powerLevel = parseInt(matches[3]);
                }
                if (!isNaN(powerLevel)) {
                    const room = MatrixClientPeg.get().getRoom(roomId);
                    if (!room) {
                        return reject("Bad room ID: " + roomId);
                    }
                    const powerLevelEvent = room.currentState.getStateEvents(
                        "m.room.power_levels", "",
                    );
                    return success(
                        MatrixClientPeg.get().setPowerLevel(
                            roomId, userId, powerLevel, powerLevelEvent,
                        ),
                    );
                }
            }
        }
        return reject(this.getUsage());
    }),

    // Reset the power level of a user
    deop: new Command("deop", "<userId>", function(roomId, args) {
        if (args) {
            const matches = args.match(/^(\S+)$/);
            if (matches) {
                const room = MatrixClientPeg.get().getRoom(roomId);
                if (!room) {
                    return reject("Bad room ID: " + roomId);
                }

                const powerLevelEvent = room.currentState.getStateEvents(
                    "m.room.power_levels", "",
                );
                return success(
                    MatrixClientPeg.get().setPowerLevel(
                        roomId, args, undefined, powerLevelEvent,
                    ),
                );
            }
        }
        return reject(this.getUsage());
    }),

    // Verify a user, device, and pubkey tuple
    verify: new Command("verify", "<userId> <deviceId> <deviceSigningKey>", function(roomId, args) {
        if (args) {
            const matches = args.match(/^(\S+) +(\S+) +(\S+)$/);
            if (matches) {
                const userId = matches[1];
                const deviceId = matches[2];
                const fingerprint = matches[3];

                return success(
                    // Promise.resolve to handle transition from static result to promise; can be removed
                    // in future
                    Promise.resolve(MatrixClientPeg.get().getStoredDevice(userId, deviceId)).then((device) => {
                        if (!device) {
                            throw new Error(_t(`Unknown (user, device) pair:`) + ` (${userId}, ${deviceId})`);
                        }

                        if (device.isVerified()) {
                            if (device.getFingerprint() === fingerprint) {
                                throw new Error(_t(`Device already verified!`));
                            } else {
                                throw new Error(_t(`WARNING: Device already verified, but keys do NOT MATCH!`));
                            }
                        }

                        if (device.getFingerprint() !== fingerprint) {
                            const fprint = device.getFingerprint();
                            throw new Error(
                                _t('WARNING: KEY VERIFICATION FAILED! The signing key for %(userId)s and device' +
                                   ' %(deviceId)s is "%(fprint)s" which does not match the provided key' +
                                   ' "%(fingerprint)s". This could mean your communications are being intercepted!',
                                   {deviceId: deviceId, fprint: fprint, userId: userId, fingerprint: fingerprint}));
                        }

                        return MatrixClientPeg.get().setDeviceVerified(userId, deviceId, true);
                    }).then(() => {
                        // Tell the user we verified everything
                        const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
                        Modal.createTrackedDialog('Slash Commands', 'Verified key', QuestionDialog, {
                            title: _t("Verified key"),
                            description: (

                                    {
                                        _t("The signing key you provided matches the signing key you received " +
                                           "from %(userId)s's device %(deviceId)s. Device marked as verified.",
                                           {userId: userId, deviceId: deviceId})
                                    }

                            ),
                            hasCancelButton: false,
                        });
                    }),
                );
            }
        }
        return reject(this.getUsage());
    }),
};
/* eslint-enable babel/no-invalid-this */

// helpful aliases
const aliases = {
    j: "join",
};

module.exports = {
    /**
     * Process the given text for /commands and perform them.
     * @param {string} roomId The room in which the command was performed.
     * @param {string} input The raw text input by the user.
     * @return {Object|null} An object with the property 'error' if there was an error
     * processing the command, or 'promise' if a request was sent out.
     * Returns null if the input didn't match a command.
     */
    processInput: function(roomId, input) {
        // trim any trailing whitespace, as it can confuse the parser for
        // IRC-style commands
        input = input.replace(/\s+$/, "");
        if (input[0] === "/" && input[1] !== "/") {
            const bits = input.match(/^(\S+?)( +((.|\n)*))?$/);
            let cmd;
            let args;
            if (bits) {
                cmd = bits[1].substring(1).toLowerCase();
                args = bits[3];
            } else {
                cmd = input;
            }
            if (cmd === "me") return null;
            if (aliases[cmd]) {
                cmd = aliases[cmd];
            }
            if (commands[cmd]) {
                return commands[cmd].run(roomId, args);
            } else {
                return reject(_t("Unrecognised command:") + ' ' + input);
            }
        }
        return null; // not a command
    },

    getCommandList: function() {
        // Return all the commands plus /me and /markdown which aren't handled like normal commands
        const cmds = Object.keys(commands).sort().map(function(cmdKey) {
            return commands[cmdKey];
        });
        cmds.push(new Command("me", "<action>", function() {}));
        cmds.push(new Command("markdown", "<on|off>", function() {}));

        return cmds;
    },
};
<commit_msg>tweak comment<commit_after>/*
Copyright 2015, 2016 OpenMarket Ltd

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import MatrixClientPeg from "./MatrixClientPeg";
import dis from "./dispatcher";
import Tinter from "./Tinter";
import sdk from './index';
import { _t } from './languageHandler';
import Modal from './Modal';

class Command {
    constructor(name, paramArgs, runFn) {
        this.name = name;
        this.paramArgs = paramArgs;
        this.runFn = runFn;
    }

    getCommand() {
        return "/" + this.name;
    }

    getCommandWithArgs() {
        return this.getCommand() + " " + this.paramArgs;
    }

    run(roomId, args) {
        return this.runFn.bind(this)(roomId, args);
    }

    getUsage() {
        return _t('Usage') + ': ' + this.getCommandWithArgs();
    }
}

function reject(msg) {
    return {
        error: msg,
    };
}

function success(promise) {
    return {
        promise: promise,
    };
}

/* Disable the "unexpected this" error for these commands - all of the run
 * functions are called with `this` bound to the Command instance.
 */

/* eslint-disable babel/no-invalid-this */

const commands = {
    ddg: new Command("ddg", "<query>", function(roomId, args) {
        const ErrorDialog = sdk.getComponent('dialogs.ErrorDialog');
        // TODO Don't explain this away, actually show a search UI here.
        Modal.createTrackedDialog('Slash Commands', '/ddg is not a command', ErrorDialog, {
            title: _t('/ddg is not a command'),
            description: _t('To use it, just wait for autocomplete results to load and tab through them.'),
        });
        return success();
    }),

    // Change your nickname
    nick: new Command("nick", "<display_name>", function(roomId, args) {
        if (args) {
            return success(
                MatrixClientPeg.get().setDisplayName(args),
            );
        }
        return reject(this.getUsage());
    }),

    // Changes the colorscheme of your current room
    tint: new Command("tint", "<color1> [<color2>]", function(roomId, args) {
        if (args) {
            const matches = args.match(/^(#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}))( +(#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})))?$/);
            if (matches) {
                Tinter.tint(matches[1], matches[4]);
                const colorScheme = {};
                colorScheme.primary_color = matches[1];
                if (matches[4]) {
                    colorScheme.secondary_color = matches[4];
                }
                return success(
                    MatrixClientPeg.get().setRoomAccountData(
                        roomId, "org.matrix.room.color_scheme", colorScheme,
                    ),
                );
            }
        }
        return reject(this.getUsage());
    }),

    // Change the room topic
    topic: new Command("topic", "<topic>", function(roomId, args) {
        if (args) {
            return success(
                MatrixClientPeg.get().setRoomTopic(roomId, args),
            );
        }
        return reject(this.getUsage());
    }),

    // Invite a user
    invite: new Command("invite", "<userId>", function(roomId, args) {
        if (args) {
            const matches = args.match(/^(\S+)$/);
            if (matches) {
                return success(
                    MatrixClientPeg.get().invite(roomId, matches[1]),
                );
            }
        }
        return reject(this.getUsage());
    }),

    // Join a room
    join: new Command("join", "#alias:domain", function(roomId, args) {
        if (args) {
            const matches = args.match(/^(\S+)$/);
            if (matches) {
                let roomAlias = matches[1];
                if (roomAlias[0] !== '#') {
                    return reject(this.getUsage());
                }
                if (!roomAlias.match(/:/)) {
                    roomAlias += ':' + MatrixClientPeg.get().getDomain();
                }

                dis.dispatch({
                    action: 'view_room',
                    room_alias: roomAlias,
                    auto_join: true,
                });

                return success();
            }
        }
        return reject(this.getUsage());
    }),

    part: new Command("part", "[#alias:domain]", function(roomId, args) {
        let targetRoomId;
        if (args) {
            const matches = args.match(/^(\S+)$/);
            if (matches) {
                let roomAlias = matches[1];
                if (roomAlias[0] !== '#') {
                    return reject(this.getUsage());
                }
                if (!roomAlias.match(/:/)) {
                    roomAlias += ':' + MatrixClientPeg.get().getDomain();
                }

                // Try to find a room with this alias
                const rooms = MatrixClientPeg.get().getRooms();
                for (let i = 0; i < rooms.length; i++) {
                    const aliasEvents = rooms[i].currentState.getStateEvents(
                        "m.room.aliases",
                    );
                    for (let j = 0; j < aliasEvents.length; j++) {
                        const aliases = aliasEvents[j].getContent().aliases || [];
                        for (let k = 0; k < aliases.length; k++) {
                            if (aliases[k] === roomAlias) {
                                targetRoomId = rooms[i].roomId;
                                break;
                            }
                        }
                        if (targetRoomId) { break; }
                    }
                    if (targetRoomId) { break; }
                }
                if (!targetRoomId) {
                    return reject(_t("Unrecognised room alias:") + ' ' + roomAlias);
                }
            }
        }
        if (!targetRoomId) targetRoomId = roomId;
        return success(
            MatrixClientPeg.get().leave(targetRoomId).then(
                function() {
                    dis.dispatch({action: 'view_next_room'});
                },
            ),
        );
    }),

    // Kick a user from the room with an optional reason
    kick: new Command("kick", "<userId> [<reason>]", function(roomId, args) {
        if (args) {
            const matches = args.match(/^(\S+?)( +(.*))?$/);
            if (matches) {
                return success(
                    MatrixClientPeg.get().kick(roomId, matches[1], matches[3]),
                );
            }
        }
        return reject(this.getUsage());
    }),

    // Ban a user from the room with an optional reason
    ban: new Command("ban", "<userId> [<reason>]", function(roomId, args) {
        if (args) {
            const matches = args.match(/^(\S+?)( +(.*))?$/);
            if (matches) {
                return success(
                    MatrixClientPeg.get().ban(roomId, matches[1], matches[3]),
                );
            }
        }
        return reject(this.getUsage());
    }),

    // Unban a user from the room
    unban: new Command("unban", "<userId>", function(roomId, args) {
        if (args) {
            const matches = args.match(/^(\S+)$/);
            if (matches) {
                // Reset the user membership to "leave" to unban him
                return success(
                    MatrixClientPeg.get().unban(roomId, matches[1]),
                );
            }
        }
        return reject(this.getUsage());
    }),

    ignore: new Command("ignore", "<userId>", function(roomId, args) {
        if (args) {
            const matches = args.match(/^(\S+)$/);
            if (matches) {
                const userId = matches[1];
                const ignoredUsers = MatrixClientPeg.get().getIgnoredUsers();
                ignoredUsers.push(userId); // de-duped internally in the js-sdk
                return success(
                    MatrixClientPeg.get().setIgnoredUsers(ignoredUsers).then(() => {
                        const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
                        Modal.createTrackedDialog('Slash Commands', 'User ignored', QuestionDialog, {
                            title: _t("Ignored user"),
                            description: (

{_t("You are now ignoring %(userId)s", {userId: userId})}

                            ),
                            hasCancelButton: false,
                        });
                    }),
                );
            }
        }
        return reject(this.getUsage());
    }),

    unignore: new Command("unignore", "<userId>", function(roomId, args) {
        if (args) {
            const matches = args.match(/^(\S+)$/);
            if (matches) {
                const userId = matches[1];
                const ignoredUsers = MatrixClientPeg.get().getIgnoredUsers();
                const index = ignoredUsers.indexOf(userId);
                if (index !== -1) ignoredUsers.splice(index, 1);
                return success(
                    MatrixClientPeg.get().setIgnoredUsers(ignoredUsers).then(() => {
                        const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
                        Modal.createTrackedDialog('Slash Commands', 'User unignored', QuestionDialog, {
                            title: _t("Unignored user"),
                            description: (

{_t("You are no longer ignoring %(userId)s", {userId: userId})}

                            ),
                            hasCancelButton: false,
                        });
                    }),
                );
            }
        }
        return reject(this.getUsage());
    }),

    // Define the power level of a user
    op: new Command("op", "<userId> [<power level>]", function(roomId, args) {
        if (args) {
            const matches = args.match(/^(\S+?)( +(\d+))?$/);
            let powerLevel = 50; // default power level for op
            if (matches) {
                const userId = matches[1];
                if (matches.length === 4 && undefined !== matches[3]) {
                    powerLevel = parseInt(matches[3]);
                }
                if (!isNaN(powerLevel)) {
                    const room = MatrixClientPeg.get().getRoom(roomId);
                    if (!room) {
                        return reject("Bad room ID: " + roomId);
                    }
                    const powerLevelEvent = room.currentState.getStateEvents(
                        "m.room.power_levels", "",
                    );
                    return success(
                        MatrixClientPeg.get().setPowerLevel(
                            roomId, userId, powerLevel, powerLevelEvent,
                        ),
                    );
                }
            }
        }
        return reject(this.getUsage());
    }),

    // Reset the power level of a user
    deop: new Command("deop", "<userId>", function(roomId, args) {
        if (args) {
            const matches = args.match(/^(\S+)$/);
            if (matches) {
                const room = MatrixClientPeg.get().getRoom(roomId);
                if (!room) {
                    return reject("Bad room ID: " + roomId);
                }

                const powerLevelEvent = room.currentState.getStateEvents(
                    "m.room.power_levels", "",
                );
                return success(
                    MatrixClientPeg.get().setPowerLevel(
                        roomId, args, undefined, powerLevelEvent,
                    ),
                );
            }
        }
        return reject(this.getUsage());
    }),

    // Verify a user, device, and pubkey tuple
    verify: new Command("verify", "<userId> <deviceId> <deviceSigningKey>", function(roomId, args) {
        if (args) {
            const matches = args.match(/^(\S+) +(\S+) +(\S+)$/);
            if (matches) {
                const userId = matches[1];
                const deviceId = matches[2];
                const fingerprint = matches[3];

                return success(
                    // Promise.resolve to handle transition from static result to promise; can be removed
                    // in future
                    Promise.resolve(MatrixClientPeg.get().getStoredDevice(userId, deviceId)).then((device) => {
                        if (!device) {
                            throw new Error(_t(`Unknown (user, device) pair:`) + ` (${userId}, ${deviceId})`);
                        }

                        if (device.isVerified()) {
                            if (device.getFingerprint() === fingerprint) {
                                throw new Error(_t(`Device already verified!`));
                            } else {
                                throw new Error(_t(`WARNING: Device already verified, but keys do NOT MATCH!`));
                            }
                        }

                        if (device.getFingerprint() !== fingerprint) {
                            const fprint = device.getFingerprint();
                            throw new Error(
                                _t('WARNING: KEY VERIFICATION FAILED! The signing key for %(userId)s and device' +
                                   ' %(deviceId)s is "%(fprint)s" which does not match the provided key' +
                                   ' "%(fingerprint)s". This could mean your communications are being intercepted!',
                                   {deviceId: deviceId, fprint: fprint, userId: userId, fingerprint: fingerprint}));
                        }

                        return MatrixClientPeg.get().setDeviceVerified(userId, deviceId, true);
                    }).then(() => {
                        // Tell the user we verified everything
                        const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
                        Modal.createTrackedDialog('Slash Commands', 'Verified key', QuestionDialog, {
                            title: _t("Verified key"),
                            description: (

                                    {
                                        _t("The signing key you provided matches the signing key you received " +
                                           "from %(userId)s's device %(deviceId)s. Device marked as verified.",
                                           {userId: userId, deviceId: deviceId})
                                    }

                            ),
                            hasCancelButton: false,
                        });
                    }),
                );
            }
        }
        return reject(this.getUsage());
    }),
};
/* eslint-enable babel/no-invalid-this */

// helpful aliases
const aliases = {
    j: "join",
};

module.exports = {
    /**
     * Process the given text for /commands and perform them.
     * @param {string} roomId The room in which the command was performed.
     * @param {string} input The raw text input by the user.
     * @return {Object|null} An object with the property 'error' if there was an error
     * processing the command, or 'promise' if a request was sent out.
     * Returns null if the input didn't match a command.
     */
    processInput: function(roomId, input) {
        // trim any trailing whitespace, as it can confuse the parser for
        // IRC-style commands
        input = input.replace(/\s+$/, "");
        if (input[0] === "/" && input[1] !== "/") {
            const bits = input.match(/^(\S+?)( +((.|\n)*))?$/);
            let cmd;
            let args;
            if (bits) {
                cmd = bits[1].substring(1).toLowerCase();
                args = bits[3];
            } else {
                cmd = input;
            }
            if (cmd === "me") return null;
            if (aliases[cmd]) {
                cmd = aliases[cmd];
            }
            if (commands[cmd]) {
                return commands[cmd].run(roomId, args);
            } else {
                return reject(_t("Unrecognised command:") + ' ' + input);
            }
        }
        return null; // not a command
    },

    getCommandList: function() {
        // Return all the commands plus /me and /markdown which aren't handled like normal commands
        const cmds = Object.keys(commands).sort().map(function(cmdKey) {
            return commands[cmdKey];
        });
        cmds.push(new Command("me", "<action>", function() {}));
        cmds.push(new Command("markdown", "<on|off>", function() {}));

        return cmds;
    },
};
<|endoftext|>


<commit_before>// @flow weak

import React from 'react';
import type { Node } from 'react';
import classNames from 'classnames';
import withStyles from '../styles/withStyles';
import createSwitch from '../internal/SwitchBase';

export const styles = (theme: Object) => ({
  root: {
    display: 'inline-flex',
    width: 62,
    position: 'relative',
  },
  bar: {
    borderRadius: 7,
    display: 'block',
    position: 'absolute',
    width: 34,
    height: 14,
    top: '50%',
    marginTop: -7,
    left: '50%',
    marginLeft: -17,
    transition: theme.transitions.create(['opacity', 'background-color'], {
      duration: theme.transitions.duration.shortest,
    }),
    backgroundColor: theme.palette.type === 'light' ? '#000' : '#fff',
    opacity: theme.palette.type === 'light' ? 0.38 : 0.3,
  },
  icon: {
    boxShadow: theme.shadows[1],
    backgroundColor: 'currentColor',
    width: 20,
    height: 20,
    borderRadius: '50%',
  },
  // For SwitchBase
  default: {
    zIndex: 1,
    color: theme.palette.type === 'light' ? theme.palette.grey[50] : theme.palette.grey[400],
    transition: theme.transitions.create('transform', {
      duration: theme.transitions.duration.shortest,
    }),
  },
  checked: {
    color: theme.palette.primary[500],
    transform: 'translateX(14px)',
    '& + $bar': {
      backgroundColor: theme.palette.primary[500],
      opacity: 0.5,
    },
  },
  disabled: {
    color: theme.palette.type === 'light' ? theme.palette.grey[400] : theme.palette.grey[800],
    '& + $bar': {
      backgroundColor: theme.palette.type === 'light' ? '#000' : '#fff',
      opacity: theme.palette.type === 'light' ? 0.12 : 0.1,
    },
  },
});

const SwitchBase = createSwitch();

type DefaultProps = {
  classes: Object,
};

export type Props = {
  /**
   * If `true`, the component is checked.
   */
  checked?: boolean | string,
  /**
   * The CSS class name of the root element when checked.
   */
  checkedClassName?: string,
  /**
   * The icon to display when the component is checked.
   * If a string is provided, it will be used as a font ligature.
   */
  checkedIcon?: Node,
  /**
   * Useful to extend the style applied to components.
   */
  classes?: Object,
  /**
   * @ignore
   */
  className?: string,
  /**
   * @ignore
   */
  defaultChecked?: boolean,
  /**
   * If `true`, the switch will be disabled.
   */
  disabled?: boolean,
  /**
   * The CSS class name of the root element when disabled.
   */
  disabledClassName?: string,
  /**
   * If `true`, the ripple effect will be disabled.
   */
  disableRipple?: boolean,
  /**
   * The icon to display when the component is unchecked.
   * If a string is provided, it will be used as a font ligature.
   */
  icon?: Node,
  /**
   * Properties applied to the `input` element.
   */
  inputProps?: Object,
  /**
   * Use that property to pass a ref callback to the native input component.
   */
  inputRef?: Function,
  /*
   * @ignore
   */
  name?: string,
  /**
   * Callback fired when the state is changed.
   *
   * @param {object} event The event source of the callback
   * @param {boolean} checked The `checked` value of the switch
   */
  onChange?: Function,
  /**
   * @ignore
   */
  tabIndex?: number | string,
  /**
   * The value of the component.
   */
  value?: string,
};

type AllProps = DefaultProps & Props;

function Switch(props: AllProps) {
  const { classes, className, ...other } = props;
  const icon =
;

  return (

      <SwitchBase
        icon={icon}
        classes={{
          default: classes.default,
          checked: classes.checked,
          disabled: classes.disabled,
        }}
        checkedIcon={icon}
        {...other}
      />

  );
}

export default withStyles(styles, { name: 'MuiSwitch' })(Switch);
<commit_msg>[Switch] Styling bug fix on long labels (#8181)<commit_after>// @flow weak

import React from 'react';
import type { Node } from 'react';
import classNames from 'classnames';
import withStyles from '../styles/withStyles';
import createSwitch from '../internal/SwitchBase';

export const styles = (theme: Object) => ({
  root: {
    display: 'inline-flex',
    width: 62,
    position: 'relative',
    flexShrink: 0,
  },
  bar: {
    borderRadius: 7,
    display: 'block',
    position: 'absolute',
    width: 34,
    height: 14,
    top: '50%',
    marginTop: -7,
    left: '50%',
    marginLeft: -17,
    transition: theme.transitions.create(['opacity', 'background-color'], {
      duration: theme.transitions.duration.shortest,
    }),
    backgroundColor: theme.palette.type === 'light' ? '#000' : '#fff',
    opacity: theme.palette.type === 'light' ? 0.38 : 0.3,
  },
  icon: {
    boxShadow: theme.shadows[1],
    backgroundColor: 'currentColor',
    width: 20,
    height: 20,
    borderRadius: '50%',
  },
  // For SwitchBase
  default: {
    zIndex: 1,
    color: theme.palette.type === 'light' ? theme.palette.grey[50] : theme.palette.grey[400],
    transition: theme.transitions.create('transform', {
      duration: theme.transitions.duration.shortest,
    }),
  },
  checked: {
    color: theme.palette.primary[500],
    transform: 'translateX(14px)',
    '& + $bar': {
      backgroundColor: theme.palette.primary[500],
      opacity: 0.5,
    },
  },
  disabled: {
    color: theme.palette.type === 'light' ? theme.palette.grey[400] : theme.palette.grey[800],
    '& + $bar': {
      backgroundColor: theme.palette.type === 'light' ? '#000' : '#fff',
      opacity: theme.palette.type === 'light' ? 0.12 : 0.1,
    },
  },
});

const SwitchBase = createSwitch();

type DefaultProps = {
  classes: Object,
};

export type Props = {
  /**
   * If `true`, the component is checked.
   */
  checked?: boolean | string,
  /**
   * The CSS class name of the root element when checked.
   */
  checkedClassName?: string,
  /**
   * The icon to display when the component is checked.
   * If a string is provided, it will be used as a font ligature.
   */
  checkedIcon?: Node,
  /**
   * Useful to extend the style applied to components.
   */
  classes?: Object,
  /**
   * @ignore
   */
  className?: string,
  /**
   * @ignore
   */
  defaultChecked?: boolean,
  /**
   * If `true`, the switch will be disabled.
   */
  disabled?: boolean,
  /**
   * The CSS class name of the root element when disabled.
   */
  disabledClassName?: string,
  /**
   * If `true`, the ripple effect will be disabled.
   */
  disableRipple?: boolean,
  /**
   * The icon to display when the component is unchecked.
   * If a string is provided, it will be used as a font ligature.
   */
  icon?: Node,
  /**
   * Properties applied to the `input` element.
   */
  inputProps?: Object,
  /**
   * Use that property to pass a ref callback to the native input component.
   */
  inputRef?: Function,
  /*
   * @ignore
   */
  name?: string,
  /**
   * Callback fired when the state is changed.
   *
   * @param {object} event The event source of the callback
   * @param {boolean} checked The `checked` value of the switch
   */
  onChange?: Function,
  /**
   * @ignore
   */
  tabIndex?: number | string,
  /**
   * The value of the component.
   */
  value?: string,
};

type AllProps = DefaultProps & Props;

function Switch(props: AllProps) {
  const { classes, className, ...other } = props;
  const icon =
;

  return (

      <SwitchBase
        icon={icon}
        classes={{
          default: classes.default,
          checked: classes.checked,
          disabled: classes.disabled,
        }}
        checkedIcon={icon}
        {...other}
      />

  );
}

export default withStyles(styles, { name: 'MuiSwitch' })(Switch);
<|endoftext|>