<commit_before>/**
 * Piano
 * Desktop browser keyboard for touch screens
 *
 * @author hugohil <hugo@soixantecircuits.fr>
 * @license MIT
 * @version 1.0b
 *
 * @todo:
 *   - Support hammer.js and/or other touch events librarie
 *   - Add space, backspace, etc
 */

var piano = (function (k){
  'use strict';

  var k = {};

  k.init = function (){
    k.container = document.createElement('div');
    k.container.id = 'piano';
    k.container.className = "piano-container";

    k.triggers = document.querySelectorAll('input[data-piano]');
    for (var i = 0; i < k.triggers.length; i++) {
      createKeyboard(k, k.triggers[i]);
    }
    document.body.appendChild(k.container);

    // Make sure to hide keyboard when clicking outside
    document.addEventListener('click', function (event){
      if(event.target.dataset.piano != '' && !k.container.contains(event.target)){
        k.hideKeyboard();
      }
    });
  }

  function createKeyboard (parent, target){
    var _k = clone(parent);
    delete _k.triggers;
    delete _k.layouts;
    var datas = target.dataset;
    var options = {};

    _k.shift = false;

    if(datas.pianoPosition.indexOf('absolute') > -1){
      options.position = datas.pianoPosition.replace(/absolute,\s/gi, '').split(',');
    } else {
      options.position = (datas.pianoPosition) ? datas.pianoPosition.split(',') : [];
    }
    options.layout = datas.pianoLayout;

    _k.settings = {
      position: {
        x: options.position[0] || 'center',
        y: options.position[1] || 'bottom'
      },
      layout: options.layout || 'default'
    }

    var events = ['click', 'touchend'];
    for (var i = 0; i < events.length; i++) {
      target.addEventListener(events[i], function (event){
        triggerHandler(event);
      });
    };

    function triggerHandler (event){
      k.currentTarget = event.target;
      k.hideKeyboard();
      displayKeyboard(_k);
    }
  }

  function displayKeyboard (instance){
    var list = document.createElement('ul');
    list.id = 'piano-list';
    k.currentKeyboard = instance;

    var layout = k.layouts[instance.settings.layout];

    for(var i in layout){
      var li = document.createElement('li');
      if(layout[i] == 'break'){
        li.className = 'break';
      } else {
        var key = document.createElement('button');
        if(typeof(layout[i][0]) == 'object'){
          key.className = 'key ' + layout[i][0].name;
          key.textContent = layout[i][0].value;
        } else {
          key.className = 'key ' + layout[i];
          key.textContent = layout[i][0];
        }
        key.addEventListener('click', function (event){
          keyPressed(event);
        });
        li.appendChild(key)
      }

      list.appendChild(li);
    }

    if(isNaN(instance.settings.position.x) || isNaN(instance.settings.position.y)){
      k.container.className += ' ' + instance.settings.position.x;
      k.container.className += ' ' + instance.settings.position.y;
    } else {
      k.container.style.left = instance.settings.position.x + 'px';
      k.container.style.top = instance.settings.position.y + 'px';
    }

    k.container.appendChild(list);
  }

  function keyPressed (event){
    k.currentTarget.focus();
    var target = event.target;
    var value = target.textContent;

    if(/del/i.test(target.className)){
      k.currentTarget.value = k.currentTarget.value.substr(0, k.currentTarget.value.length - 1);
    } else if(/space/i.test(target.className)){
      k.currentTarget.value += ' ';
    } else if(/shift/i.test(target.className)){
      k.switchCase();
    } else {
      k.currentTarget.value += value;
    }
  }

  k.switchCase = function (){
    var shift = k.currentKeyboard.shift = !k.currentKeyboard.shift;
    var keys = document.querySelectorAll('#piano-list li');
    var layout = k.layouts[k.currentKeyboard.settings.layout];

    for (var i = 0; i < keys.length; i++) {
      var target = keys[i].children[0];
      if(target){
        var value = target.textContent;
        if(layout[i].length > 1 && layout[i][0].length > 0){
          target.textContent = (shift) ? layout[i][1] : layout[i][0];
        } else {
          target.textContent = (shift) ? value.toUpperCase() : value.toLowerCase();
        }
      }
    }
  }

  k.hideKeyboard = function (){
    if(k.container.firstChild){
      k.container.firstChild.remove();
      k.container.style.top = k.container.style.left = '';
      k.container.className = "tk-container";
      k.currentKeyboard = null;
    }
  }

  function clone (src) {
    var dest = {};
    for (var key in src) {
        dest[key] = src[key];
    }
    return dest;
  }

  return k;
})(piano || {});<commit_msg>Fix last commit<commit_after>/**
 * Piano
 * Desktop browser keyboard for touch screens
 *
 * @author hugohil <hugo@soixantecircuits.fr>
 * @license MIT
 * @version 1.0b
 *
 * @todo:
 *   - Support hammer.js and/or other touch events librarie
 *   - Add space, backspace, etc
 */

var piano = (function (k){
  'use strict';

  var k = {};

  k.init = function (){
    k.container = document.createElement('div');
    k.container.id = 'piano';
    k.container.className = "piano-container";

    k.triggers = document.querySelectorAll('input[data-piano]');
    for (var i = 0; i < k.triggers.length; i++) {
      createKeyboard(k, k.triggers[i]);
    }
    document.body.appendChild(k.container);

    // Make sure to hide keyboard when clicking outside
    document.addEventListener('click', function (event){
      if(event.target.dataset.piano != '' && !k.container.contains(event.target)){
        k.hideKeyboard();
      }
    });
  }

  function createKeyboard (parent, target){
    var _k = clone(parent);
    delete _k.triggers;
    delete _k.layouts;
    var datas = target.dataset;
    var options = {};

    _k.shift = false;

    if(datas.pianoPosition.indexOf('absolute') > -1){
      options.position = datas.pianoPosition.replace(/absolute,\s/gi, '').split(',');
    } else {
      options.position = (datas.pianoPosition) ? datas.pianoPosition.split(',') : [];
    }
    options.layout = datas.pianoLayout;

    _k.settings = {
      position: {
        x: options.position[0] || 'center',
        y: options.position[1] || 'bottom'
      },
      layout: options.layout || 'default'
    }

    var events = ['click', 'touchend'];
    for (var i = 0; i < events.length; i++) {
      target.addEventListener(events[i], function (event){
        triggerHandler(event);
      });
    };

    function triggerHandler (event){
      k.currentTarget = event.target;
      k.hideKeyboard();
      displayKeyboard(_k);
    }
  }

  function displayKeyboard (instance){
    var list = document.createElement('ul');
    list.id = 'piano-list';
    k.currentKeyboard = instance;

    var layout = k.layouts[instance.settings.layout];

    for(var i in layout){
      var li = document.createElement('li');
      if(layout[i] == 'break'){
        li.className = 'break';
      } else {
        var key = document.createElement('button');
        if(typeof(layout[i][0]) == 'object'){
          key.className = 'key ' + layout[i][0].name;
          key.textContent = layout[i][0].value;
        } else {
          key.className = 'key ' + layout[i];
          key.textContent = layout[i][0];
        }
        key.addEventListener('click', function (event){
          keyPressed(event);
        });
        li.appendChild(key)
      }

      list.appendChild(li);
    }

    if(isNaN(instance.settings.position.x) || isNaN(instance.settings.position.y)){
      k.container.className += ' ' + instance.settings.position.x;
      k.container.className += ' ' + instance.settings.position.y;
    } else {
      k.container.style.left = instance.settings.position.x + 'px';
      k.container.style.top = instance.settings.position.y + 'px';
    }

    k.container.appendChild(list);
  }

  function keyPressed (event){
    k.currentTarget.focus();
    var target = event.target;
    var value = target.textContent;

    if(/del/i.test(target.className)){
      k.currentTarget.value = k.currentTarget.value.substr(0, k.currentTarget.value.length - 1);
    } else if(/space/i.test(target.className)){
      k.currentTarget.value += ' ';
    } else if(/shift/i.test(target.className)){
      k.switchCase();
    } else {
      k.currentTarget.value += value;
    }
  }

  k.switchCase = function (){
    var shift = k.currentKeyboard.shift = !k.currentKeyboard.shift;
    var keys = document.querySelectorAll('#piano-list li');
    var layout = k.layouts[k.currentKeyboard.settings.layout];

    for (var i = 0; i < keys.length; i++) {
      var target = keys[i].children[0];
      if(target){
        var value = target.textContent;
        if(layout[i].length > 1 && layout[i][0].length > 0){
          target.textContent = (shift) ? layout[i][1] : layout[i][0];
        } else {
          target.textContent = (shift) ? value.toUpperCase() : value.toLowerCase();
        }
      }
    }
  }

  k.hideKeyboard = function (){
    if(k.container.firstChild){
      k.container.firstChild.remove();
      k.container.style.top = k.container.style.left = '';
      k.container.className = "piano-container";
      k.currentKeyboard = null;
    }
  }

  function clone (src) {
    var dest = {};
    for (var key in src) {
        dest[key] = src[key];
    }
    return dest;
  }

  return k;
})(piano || {});<|endoftext|>


<commit_before>( function ( $, mw ) {

	// Load this script after lib/mapbox-lib.js

	var scale, urlFormat, windowManager, mapDialog,
		mapServer = mw.config.get( 'wgKartographerMapServer' ),
		forceHttps = mapServer[ 4 ] === 's',
		config = L.mapbox.config;

	config.REQUIRE_ACCESS_TOKEN = false;
	config.FORCE_HTTPS = forceHttps;
	config.HTTP_URL = forceHttps ? false : mapServer;
	config.HTTPS_URL = !forceHttps ? false : mapServer;

	function bracketDevicePixelRatio() {
		var i, scale,
			brackets = mw.config.get( 'wgKartographerSrcsetScales' ),
			baseRatio = window.devicePixelRatio || 1;
		if ( !brackets ) {
			return 1;
		}
		brackets.unshift( 1 );
		for ( i = 0; i < brackets.length; i++ ) {
			scale = brackets[ i ];
			if ( scale >= baseRatio || ( baseRatio - scale ) < 0.1 ) {
				return scale;
			}
		}
		return brackets[ brackets.length - 1 ];
	}

	function isPrivateGroup( groupName ) {
		return groupName[ 0 ] === '_';
	}

	scale = bracketDevicePixelRatio();
	scale = ( scale === 1 ) ? '' : ( '@' + scale + 'x' );
	urlFormat = '/{z}/{x}/{y}' + scale + '.png';

	mw.kartographer = {};

	/**
	 * Create a new interactive map
	 *
	 * @param {HTMLElement} container Map container
	 * @param {Object} data Map data
	 * @param {number} data.latitude Latitude
	 * @param {number} data.longitude Longitude
	 * @param {number} data.zoom Zoom
	 * @param {string} [data.style] Map style
	 * @param {string[]} [data.overlays] Names of overlay groups to show
	 * @return {L.mapbox.Map} Map object
	 */
	mw.kartographer.createMap = function ( container, data ) {
		var map,
			style = data.style || mw.config.get( 'wgKartographerDfltStyle' ),
			mapData = mw.config.get( 'wgKartographerLiveData' ) || {};

		map = L.map( container );
		if ( !container.clientWidth ) {
			// HACK: If the container is not naturally measurable, try jQuery
			// which will pick up CSS dimensions. T125263
			/*jscs:disable disallowDanglingUnderscores */
			map._size = new L.Point(
				$( container ).width(),
				$( container ).height()
			);
			/*jscs:enable disallowDanglingUnderscores */
		}
		map.setView( [ data.latitude, data.longitude ], data.zoom );
		map.attributionControl.setPrefix( '' );
		L.tileLayer( mapServer + '/' + style + urlFormat, {
			maxZoom: 18,
			attribution: mw.message( 'kartographer-attribution' ).parse()
		} ).addTo( map );

		if ( data.overlays ) {
			$.each( data.overlays, function ( index, group ) {
				if ( group === '*' ) {
					$.each( mapData, function ( k, d ) {
						if ( !isPrivateGroup( k ) ) {
							mw.kartographer.addDataLayer( map, d );
						}
					} );
				} else if ( mapData.hasOwnProperty( group ) ) {
					if ( index + 1 === data.overlays.length ) {
						map.kartographerLayer =
							mw.kartographer.addDataLayer( map, mapData[ group ] );
					} else {
						mw.kartographer.addDataLayer( map, mapData[ group ] );
					}
				}
			} );
		}

		return map;
	};

	/**
	 * Create a new GeoJSON layer and add it to map.
	 *
	 * @param {L.mapbox.Map} map Map to get layers from
	 * @param {Object} geoJson
	 */
	mw.kartographer.addDataLayer = function ( map, geoJson ) {
		try {
			return L.mapbox.featureLayer( geoJson ).addTo( map );
		} catch ( e ) {
			mw.log( e );
		}
	};

	/**
	 * Get "editable" geojson layer for the map.
	 *
	 * If a layer doesn't exist, create and attach one.
	 *
	 * @param {L.mapbox.Map} map Map to get layers from
	 * @param {L.mapbox.FeatureLayer} map.kartographerLayer show tag-specific info in this layer
	 * @return {L.mapbox.FeatureLayer|null} GeoJSON layer, if present
	 */
	mw.kartographer.getKartographerLayer = function ( map ) {
		if ( !map.kartographerLayer ) {
			map.kartographerLayer = L.mapbox.featureLayer().addTo( map );
		}
		return map.kartographerLayer;
	};

	/**
	 * Update "editable" geojson layer from a string
	 *
	 * @param {L.mapbox.Map} map Map to set the GeoJSON for
	 * @param {string} geoJsonString GeoJSON data, empty string to clear
	 * @return {boolean} The GeoJSON string provided was valid as was applied
	 */
	mw.kartographer.updateKartographerLayer = function ( map, geoJsonString ) {
		var geoJson, layer, isValid = true;

		if ( geoJsonString ) {
			try {
				geoJson = JSON.parse( geoJsonString );
			} catch ( e ) {
				// Invalid JSON, clear it
				isValid = false;
			}
		}

		try {
			layer = mw.kartographer.getKartographerLayer( map );
			layer.setGeoJSON( !isValid || geoJson === undefined ? [] : geoJson );
			return isValid;
		} catch ( e ) {
			return false;
		}
	};

	function getWindowManager() {
		if ( !windowManager ) {
			windowManager = new OO.ui.WindowManager();
			mapDialog = new mw.kartographer.MapDialog();
			$( 'body' ).append( windowManager.$element );
			windowManager.addWindows( [ mapDialog ] );
		}
		return windowManager;
	}

	/**
	 * Open a full screen map
	 *
	 * @param {Object} data Map data
	 * @param {L.mapbox.Map} [map] Optional map to get current state from
	 */
	mw.kartographer.openFullscreenMap = function ( data, map ) {
		mw.loader.using( 'ext.kartographer.fullscreen' ).done( function () {
			var center;
			if ( map ) {
				center = map.getCenter();
				data.latitude = center.lat;
				data.longitude = center.lng;
				data.zoom = map.getZoom();
			}
			getWindowManager().openWindow( mapDialog, data );
		} );
	};

	function getMapData( $el ) {
		// Prevent users from adding map divs directly via wikitext
		if ( $el.attr( 'mw-data' ) !== 'interface' ) {
			return;
		}

		return {
			latitude: +$el.data( 'lat' ),
			longitude: +$el.data( 'lon' ),
			zoom: +$el.data( 'zoom' ),
			style: $el.data( 'style' ),
			overlays: $el.data( 'overlays' )
		};
	}

	mw.hook( 'wikipage.content' ).add( function ( $content ) {
		$content.find( '.mw-kartographer-link' ).each( function () {
			var $this = $( this ),
				data = getMapData( $this );

			if ( data ) {
				$this.on( 'click', function () {
					mw.kartographer.openFullscreenMap( data );
					return false;
				} );
			}
		} );

		$content.find( '.mw-kartographer-interactive' ).each( function () {
			var map,
				$this = $( this ),
				data = getMapData( $this );

			if ( data ) {
				map = mw.kartographer.createMap( this, data );

				// TODO: Bind this to a fullscreen button in the map as well

				map.doubleClickZoom.disable();
				$this.on( 'dblclick', function () {
					mw.kartographer.openFullscreenMap( data, map );
				} );
			}
		} );
	} );
}( jQuery, mediaWiki ) );
<commit_msg>Set frame map to popup's position<commit_after>( function ( $, mw ) {

	// Load this script after lib/mapbox-lib.js

	var scale, urlFormat, windowManager, mapDialog,
		mapServer = mw.config.get( 'wgKartographerMapServer' ),
		forceHttps = mapServer[ 4 ] === 's',
		config = L.mapbox.config;

	config.REQUIRE_ACCESS_TOKEN = false;
	config.FORCE_HTTPS = forceHttps;
	config.HTTP_URL = forceHttps ? false : mapServer;
	config.HTTPS_URL = !forceHttps ? false : mapServer;

	function bracketDevicePixelRatio() {
		var i, scale,
			brackets = mw.config.get( 'wgKartographerSrcsetScales' ),
			baseRatio = window.devicePixelRatio || 1;
		if ( !brackets ) {
			return 1;
		}
		brackets.unshift( 1 );
		for ( i = 0; i < brackets.length; i++ ) {
			scale = brackets[ i ];
			if ( scale >= baseRatio || ( baseRatio - scale ) < 0.1 ) {
				return scale;
			}
		}
		return brackets[ brackets.length - 1 ];
	}

	function isPrivateGroup( groupName ) {
		return groupName[ 0 ] === '_';
	}

	scale = bracketDevicePixelRatio();
	scale = ( scale === 1 ) ? '' : ( '@' + scale + 'x' );
	urlFormat = '/{z}/{x}/{y}' + scale + '.png';

	mw.kartographer = {};

	/**
	 * Create a new interactive map
	 *
	 * @param {HTMLElement} container Map container
	 * @param {Object} data Map data
	 * @param {number} data.latitude Latitude
	 * @param {number} data.longitude Longitude
	 * @param {number} data.zoom Zoom
	 * @param {string} [data.style] Map style
	 * @param {string[]} [data.overlays] Names of overlay groups to show
	 * @return {L.mapbox.Map} Map object
	 */
	mw.kartographer.createMap = function ( container, data ) {
		var map,
			style = data.style || mw.config.get( 'wgKartographerDfltStyle' ),
			mapData = mw.config.get( 'wgKartographerLiveData' ) || {};

		map = L.map( container );
		if ( !container.clientWidth ) {
			// HACK: If the container is not naturally measurable, try jQuery
			// which will pick up CSS dimensions. T125263
			/*jscs:disable disallowDanglingUnderscores */
			map._size = new L.Point(
				$( container ).width(),
				$( container ).height()
			);
			/*jscs:enable disallowDanglingUnderscores */
		}
		map.setView( [ data.latitude, data.longitude ], data.zoom );
		map.attributionControl.setPrefix( '' );
		L.tileLayer( mapServer + '/' + style + urlFormat, {
			maxZoom: 18,
			attribution: mw.message( 'kartographer-attribution' ).parse()
		} ).addTo( map );

		if ( data.overlays ) {
			$.each( data.overlays, function ( index, group ) {
				if ( group === '*' ) {
					$.each( mapData, function ( k, d ) {
						if ( !isPrivateGroup( k ) ) {
							mw.kartographer.addDataLayer( map, d );
						}
					} );
				} else if ( mapData.hasOwnProperty( group ) ) {
					if ( index + 1 === data.overlays.length ) {
						map.kartographerLayer =
							mw.kartographer.addDataLayer( map, mapData[ group ] );
					} else {
						mw.kartographer.addDataLayer( map, mapData[ group ] );
					}
				}
			} );
		}

		return map;
	};

	/**
	 * Create a new GeoJSON layer and add it to map.
	 *
	 * @param {L.mapbox.Map} map Map to get layers from
	 * @param {Object} geoJson
	 */
	mw.kartographer.addDataLayer = function ( map, geoJson ) {
		try {
			return L.mapbox.featureLayer( geoJson ).addTo( map );
		} catch ( e ) {
			mw.log( e );
		}
	};

	/**
	 * Get "editable" geojson layer for the map.
	 *
	 * If a layer doesn't exist, create and attach one.
	 *
	 * @param {L.mapbox.Map} map Map to get layers from
	 * @param {L.mapbox.FeatureLayer} map.kartographerLayer show tag-specific info in this layer
	 * @return {L.mapbox.FeatureLayer|null} GeoJSON layer, if present
	 */
	mw.kartographer.getKartographerLayer = function ( map ) {
		if ( !map.kartographerLayer ) {
			map.kartographerLayer = L.mapbox.featureLayer().addTo( map );
		}
		return map.kartographerLayer;
	};

	/**
	 * Update "editable" geojson layer from a string
	 *
	 * @param {L.mapbox.Map} map Map to set the GeoJSON for
	 * @param {string} geoJsonString GeoJSON data, empty string to clear
	 * @return {boolean} The GeoJSON string provided was valid as was applied
	 */
	mw.kartographer.updateKartographerLayer = function ( map, geoJsonString ) {
		var geoJson, layer, isValid = true;

		if ( geoJsonString ) {
			try {
				geoJson = JSON.parse( geoJsonString );
			} catch ( e ) {
				// Invalid JSON, clear it
				isValid = false;
			}
		}

		try {
			layer = mw.kartographer.getKartographerLayer( map );
			layer.setGeoJSON( !isValid || geoJson === undefined ? [] : geoJson );
			return isValid;
		} catch ( e ) {
			return false;
		}
	};

	function getWindowManager() {
		if ( !windowManager ) {
			windowManager = new OO.ui.WindowManager();
			mapDialog = new mw.kartographer.MapDialog();
			$( 'body' ).append( windowManager.$element );
			windowManager.addWindows( [ mapDialog ] );
		}
		return windowManager;
	}

	/**
	 * Open a full screen map
	 *
	 * @param {Object} data Map data
	 * @param {L.mapbox.Map} [map] Optional map to get current state from
	 */
	mw.kartographer.openFullscreenMap = function ( data, map ) {
		mw.loader.using( 'ext.kartographer.fullscreen' ).done( function () {
			var center;
			if ( map ) {
				center = map.getCenter();
				data.latitude = center.lat;
				data.longitude = center.lng;
				data.zoom = map.getZoom();
			}
			getWindowManager()
				.openWindow( mapDialog, data )
				.then( function ( opened ) { return opened; } )
				.then( function ( closing ) {
					map.setView( mapDialog.map.getCenter(), mapDialog.map.getZoom() );
					return closing;
				} );
		} );
	};

	function getMapData( $el ) {
		// Prevent users from adding map divs directly via wikitext
		if ( $el.attr( 'mw-data' ) !== 'interface' ) {
			return;
		}

		return {
			latitude: +$el.data( 'lat' ),
			longitude: +$el.data( 'lon' ),
			zoom: +$el.data( 'zoom' ),
			style: $el.data( 'style' ),
			overlays: $el.data( 'overlays' )
		};
	}

	mw.hook( 'wikipage.content' ).add( function ( $content ) {
		$content.find( '.mw-kartographer-link' ).each( function () {
			var $this = $( this ),
				data = getMapData( $this );

			if ( data ) {
				$this.on( 'click', function () {
					mw.kartographer.openFullscreenMap( data );
					return false;
				} );
			}
		} );

		$content.find( '.mw-kartographer-interactive' ).each( function () {
			var map,
				$this = $( this ),
				data = getMapData( $this );

			if ( data ) {
				map = mw.kartographer.createMap( this, data );

				// TODO: Bind this to a fullscreen button in the map as well

				map.doubleClickZoom.disable();
				$this.on( 'dblclick', function () {
					mw.kartographer.openFullscreenMap( data, map );
				} );
			}
		} );
	} );
}( jQuery, mediaWiki ) );
<|endoftext|>


<commit_before>( function ( $, mw ) {

	// Load this script after lib/mapbox-lib.js

	var scale, urlFormat,
		mapServer = mw.config.get( 'wgKartographerMapServer' ),
		forceHttps = mapServer[ 4 ] === 's',
		config = L.mapbox.config;

	config.REQUIRE_ACCESS_TOKEN = false;
	config.FORCE_HTTPS = forceHttps;
	config.HTTP_URL = forceHttps ? false : mapServer;
	config.HTTPS_URL = !forceHttps ? false : mapServer;

	function bracketDevicePixelRatio() {
		var i, scale,
			brackets = mw.config.get( 'wgKartographerSrcsetScales' ),
			baseRatio = window.devicePixelRatio || 1;
		if ( !brackets ) {
			return 1;
		}
		brackets.unshift( 1 );
		for ( i = 0; i < brackets.length; i++ ) {
			scale = brackets[ i ];
			if ( scale >= baseRatio || ( baseRatio - scale ) < 0.1 ) {
				return scale;
			}
		}
		return brackets[ brackets.length - 1 ];
	}

	scale = ( bracketDevicePixelRatio() === 1 ) ? '' : ( '@' + scale + 'x' );
	urlFormat = '/{z}/{x}/{y}' + scale + '.png';

	mw.kartographer = {};

	/**
	 * Create a new interactive map
	 *
	 * @param {HTMLElement} container Map container
	 * @param {Object} data Map data
	 * @param {number} data.latitude Latitude
	 * @param {number} data.longitude Longitude
	 * @param {number} data.zoom Zoom
	 * @param {string} [data.style] Map style
	 * @param {Object} [data.geoJson] Raw GeoJSON
	 * @param {Object} [data.overlays] Overlays
	 * @return {L.map} Map object
	 */
	mw.kartographer.createMap = function ( container, data ) {
		var dataLayer, geoJson, map,
			style = data.style || mw.config.get( 'wgKartographerDfltStyle' ),
			mapData = mw.config.get( 'wgKartographerLiveData' ) || {};

		map = L.map( container );
		if ( !container.clientWidth ) {
			// HACK: If the container is not naturally measureable, try jQuery
			// which will pick up CSS dimensions. T125263
			/*jscs:disable disallowDanglingUnderscores */
			map._size = new L.Point(
				$( container ).width(),
				$( container ).height()
			);
			/*jscs:enable disallowDanglingUnderscores */
		}
		map.setView( [ data.latitude, data.longitude ], data.zoom );
		map.attributionControl.setPrefix( '' );
		L.tileLayer( mapServer + '/' + style + urlFormat, {
			maxZoom: 18,
			attribution: mw.message( 'kartographer-attribution' ).parse()
		} ).addTo( map );

		geoJson = data.geoJson || [];

		if ( data.overlays ) {
			geoJson = [];
			$.each( data.overlays, function ( _, group ) {
				if ( group === '*' ) {
					$.each( mapData, function ( k, d ) {
						if ( k[ 0 ] !== '_' ) {
							geoJson = geoJson.concat( d );
						}
					} );
				} else if ( mapData.hasOwnProperty( group ) ) {
					geoJson = geoJson.concat( mapData[ group ] );
				}
			} );
		}
		if ( geoJson.length ) {
			dataLayer = L.mapbox.featureLayer().addTo( map );
			dataLayer.setGeoJSON( geoJson );
		}

		return map;
	};

	mw.hook( 'wikipage.content' ).add( function ( $content ) {
		$content.find( '.mw-kartographer-interactive' ).each( function () {
			var $this = $( this );

			mw.kartographer.createMap( this, {
				latitude: +$this.data( 'lat' ),
				longitude: +$this.data( 'lon' ),
				zoom: +$this.data( 'zoom' ),
				style: $this.data( 'style' ),
				overlays: $this.data( 'overlays' )
			} );
		} );
	} );
}( jQuery, mediaWiki ) );
<commit_msg>Fixed map scaling<commit_after>( function ( $, mw ) {

	// Load this script after lib/mapbox-lib.js

	var scale, urlFormat,
		mapServer = mw.config.get( 'wgKartographerMapServer' ),
		forceHttps = mapServer[ 4 ] === 's',
		config = L.mapbox.config;

	config.REQUIRE_ACCESS_TOKEN = false;
	config.FORCE_HTTPS = forceHttps;
	config.HTTP_URL = forceHttps ? false : mapServer;
	config.HTTPS_URL = !forceHttps ? false : mapServer;

	function bracketDevicePixelRatio() {
		var i, scale,
			brackets = mw.config.get( 'wgKartographerSrcsetScales' ),
			baseRatio = window.devicePixelRatio || 1;
		if ( !brackets ) {
			return 1;
		}
		brackets.unshift( 1 );
		for ( i = 0; i < brackets.length; i++ ) {
			scale = brackets[ i ];
			if ( scale >= baseRatio || ( baseRatio - scale ) < 0.1 ) {
				return scale;
			}
		}
		return brackets[ brackets.length - 1 ];
	}

	scale = bracketDevicePixelRatio();
	scale = ( scale === 1 ) ? '' : ( '@' + scale + 'x' );
	urlFormat = '/{z}/{x}/{y}' + scale + '.png';

	mw.kartographer = {};

	/**
	 * Create a new interactive map
	 *
	 * @param {HTMLElement} container Map container
	 * @param {Object} data Map data
	 * @param {number} data.latitude Latitude
	 * @param {number} data.longitude Longitude
	 * @param {number} data.zoom Zoom
	 * @param {string} [data.style] Map style
	 * @param {Object} [data.geoJson] Raw GeoJSON
	 * @param {Object} [data.overlays] Overlays
	 * @return {L.map} Map object
	 */
	mw.kartographer.createMap = function ( container, data ) {
		var dataLayer, geoJson, map,
			style = data.style || mw.config.get( 'wgKartographerDfltStyle' ),
			mapData = mw.config.get( 'wgKartographerLiveData' ) || {};

		map = L.map( container );
		if ( !container.clientWidth ) {
			// HACK: If the container is not naturally measureable, try jQuery
			// which will pick up CSS dimensions. T125263
			/*jscs:disable disallowDanglingUnderscores */
			map._size = new L.Point(
				$( container ).width(),
				$( container ).height()
			);
			/*jscs:enable disallowDanglingUnderscores */
		}
		map.setView( [ data.latitude, data.longitude ], data.zoom );
		map.attributionControl.setPrefix( '' );
		L.tileLayer( mapServer + '/' + style + urlFormat, {
			maxZoom: 18,
			attribution: mw.message( 'kartographer-attribution' ).parse()
		} ).addTo( map );

		geoJson = data.geoJson || [];

		if ( data.overlays ) {
			geoJson = [];
			$.each( data.overlays, function ( _, group ) {
				if ( group === '*' ) {
					$.each( mapData, function ( k, d ) {
						if ( k[ 0 ] !== '_' ) {
							geoJson = geoJson.concat( d );
						}
					} );
				} else if ( mapData.hasOwnProperty( group ) ) {
					geoJson = geoJson.concat( mapData[ group ] );
				}
			} );
		}
		if ( geoJson.length ) {
			dataLayer = L.mapbox.featureLayer().addTo( map );
			dataLayer.setGeoJSON( geoJson );
		}

		return map;
	};

	mw.hook( 'wikipage.content' ).add( function ( $content ) {
		$content.find( '.mw-kartographer-interactive' ).each( function () {
			var $this = $( this );

			mw.kartographer.createMap( this, {
				latitude: +$this.data( 'lat' ),
				longitude: +$this.data( 'lon' ),
				zoom: +$this.data( 'zoom' ),
				style: $this.data( 'style' ),
				overlays: $this.data( 'overlays' )
			} );
		} );
	} );
}( jQuery, mediaWiki ) );
<|endoftext|>


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

module.exports = {
  setDB: function(newDb) {
    db = require(newDb);
  },

  addRoute: function(routeID, valid_bus_types){
    //QUERY = "INSERT INTO route "
    return db.query("INSERT INTO route VALUES ($1, $2) RETURNING *", [routeID, valid_bus_types]);
  },

  getRouteNumber: function(routeID){
    return db.query("SELECT number FROM route WHERE id=$1", [routeID]);
  },

  getAvailableDetours: function(routeID){
    return db.query("SELECT * FROM detour WHERE routes=$1", [routeID]);
  },

  getCurrentDetours: function(routeID){
    return db.query("SELECT * FROM detour WHERE routes=$1", [routeID]);
  },

  getValidBusTrpes: function(routeID){
    return db.query("SELECT valid_bus_types FROM route WHERE id=$1", [routeID]);
  },

  getAllRoutes: function(){
    return db.query("SELECT * FROM route");
  },

  editRouteNumber: function(routeID, new_number){
    return db.query("UPDATE route SET number = new_number WHERE id=$1", [routeID]);
  }

}
/*
  editValidBusTypes: function(routeID, new_bus_types){
    return db.query("UPDATE route SET valid_bus_types = new_bus_types WHERE id=$1", [routeID]);
  },

  deleteDetour: function(routeID, detourID){
    return db.query("DELETE FROM detour WHERE id = $1 AND route = $2", [detourID, routeID])
  },

  createDetour: function(routeID, detourID){
    return db.query("INSERT INTO detour (id, routes) VALUES ($1, $2)", [detourID, routeID]);
  },

  avtiveDetour: function(routeID, detourID, state){
    return db.query("UPDATE detour SET is_avtive = $1 WHERE id = $2 AND routes = $3", [state, detourID, routeID]);
  },

  editDetourPath: function(routeID, detourID, new_path){
    return db.query("UPDATE detour SET path = $1 WHERE id = $2 AND routes = $3", [new_path, detourID, routeID]);
  }

}
*/<commit_msg>delete useless line<commit_after>var db = require('./db');

module.exports = {
  setDB: function(newDb) {
    db = require(newDb);
  },

  addRoute: function(routeID, valid_bus_types){
    return db.query("INSERT INTO route VALUES ($1, $2) RETURNING *", [routeID, valid_bus_types]);
  },

  getRouteNumber: function(routeID){
    return db.query("SELECT number FROM route WHERE id=$1", [routeID]);
  },

  getAvailableDetours: function(routeID){
    return db.query("SELECT * FROM detour WHERE routes=$1", [routeID]);
  },

  getCurrentDetours: function(routeID){
    return db.query("SELECT * FROM detour WHERE routes=$1", [routeID]);
  },

  getValidBusTrpes: function(routeID){
    return db.query("SELECT valid_bus_types FROM route WHERE id=$1", [routeID]);
  },

  getAllRoutes: function(){
    return db.query("SELECT * FROM route");
  },

  editRouteNumber: function(routeID, new_number){
    return db.query("UPDATE route SET number = new_number WHERE id=$1", [routeID]);
  }

}
/*
  editValidBusTypes: function(routeID, new_bus_types){
    return db.query("UPDATE route SET valid_bus_types = new_bus_types WHERE id=$1", [routeID]);
  },

  deleteDetour: function(routeID, detourID){
    return db.query("DELETE FROM detour WHERE id = $1 AND route = $2", [detourID, routeID])
  },

  createDetour: function(routeID, detourID){
    return db.query("INSERT INTO detour (id, routes) VALUES ($1, $2)", [detourID, routeID]);
  },

  avtiveDetour: function(routeID, detourID, state){
    return db.query("UPDATE detour SET is_avtive = $1 WHERE id = $2 AND routes = $3", [state, detourID, routeID]);
  },

  editDetourPath: function(routeID, detourID, new_path){
    return db.query("UPDATE detour SET path = $1 WHERE id = $2 AND routes = $3", [new_path, detourID, routeID]);
  }

}
*/
<|endoftext|>


<commit_before>/*
  html2canvas @VERSION@ <http://html2canvas.hertzen.com>
  Copyright (c) 2011 Niklas von Hertzen. All rights reserved.
  http://www.twitter.com/niklasvh

  Released under MIT License
 */

(function(){

_html2canvas.Generate = {};

// -webkit-linear-gradient(left top, rgb(255, 0, 0), rgb(0, 0, 255), rgb(186, 218, 85), rgba(0, 0, 255, 0.496094))
// -webkit-linear-gradient(left, rgb(206, 219, 233) 0%, rgb(170, 197, 222) 17%, rgb(97, 153, 199) 50%, rgb(58, 132, 195) 51%, rgb(65, 154, 214) 59%, rgb(75, 184, 240) 71%, rgb(58, 139, 194) 84%, rgb(38, 85, 139) 100%)
// -webkit-gradient(linear, 0% 0, 0% 100%, from(rgb(252, 252, 252)), to(rgb(232, 232, 232)))
// -webkit-gradient(linear, 0% 0%, 0% 100%, from(rgb(240, 183, 161)), color-stop(0.5, rgb(140, 51, 16)), color-stop(0.51, rgb(117, 34, 1)), to(rgb(191, 110, 78)))
// -moz-linear-gradient(100% 0%, rgb(255, 0, 0), rgb(0, 0, 255), rgb(186, 218, 85), rgba(0, 0, 255, 0.5))

var reGradients = [
    /^(-webkit-linear-gradient)\(([a-z\s]+)((?:,\s(?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\)(?:\s\d{1,3}%)?)+)\)$/,
    /^(-webkit-gradient)\((linear|radial),\s((?:\d{1,3}%?)\s(?:\d{1,3}%?),\s(?:\d{1,3}%?)\s(?:\d{1,3}%?))((?:,\s(?:from|to|color-stop)\((?:[0-9\.]+,\s)?(?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\)\))+)\)$/
];

// ["-webkit-linear-gradient(left top, rgb(255, 0, 0), rgb(0, 0, 255), rgb(186, 218, 85), rgba(0, 0, 255, 0.496094))", "-webkit-linear-gradient", "left top", ", rgb(255, 0, 0), rgb(0, 0, 255), rgb(186, 218, 85), rgba(0, 0, 255, 0.496094)"]
// ["-webkit-linear-gradient(left, rgb(206, 219, 233) 0%, rgb(170, 197, 222) 17%, rgb(97, 153, 199) 50%, rgb(58, 132, 195) 51%, rgb(65, 154, 214) 59%, rgb(75, 184, 240) 71%, rgb(58, 139, 194) 84%, rgb(38, 85, 139) 100%)", "-webkit-linear-gradient", "left", ", rgb(206, 219, 233) 0%, rgb(170, 197, 222) 17%, rgb(97, 153, 199) 50%, rgb(58, 132, 195) 51%, rgb(65, 154, 214) 59%, rgb(75, 184, 240) 71%, rgb(58, 139, 194) 84%, rgb(38, 85, 139) 100%"]
// ["-webkit-gradient(linear, 0% 0, 0% 100%, from(rgb(252, 252, 252)), to(rgb(232, 232, 232)))", "-webkit-gradient", "linear", "0% 0", "0% 100%", ", from(rgb(252, 252, 252)), to(rgb(232, 232, 232))"]
// ["-webkit-gradient(linear, 0% 0%, 0% 100%, from(rgb(240, 183, 161)), color-stop(0.5, rgb(140, 51, 16)), color-stop(0.51, rgb(117, 34, 1)), to(rgb(191, 110, 78)))", "-webkit-gradient", "linear", "0% 0%, 0% 100%", ", from(rgb(240, 183, 161)), color-stop(0.5, rgb(140, 51, 16)), color-stop(0.51, rgb(117, 34, 1)), to(rgb(191, 110, 78))"]

_html2canvas.Generate.parseGradient = function(css, bounds) {
    var gradient, i, len = reGradients.length, m1, stop, m2, m2Len, step, m3;

    for(i = 0; i < len; i+=1){
        m1 = css.match(reGradients[i]);
        if(m1) break;
    }

    if(m1) {
        switch(m1[1]) {
            case '-webkit-linear-gradient':

                gradient = {
                    type: 'linear',
                    x0: 0,
                    y0: 0,
                    x1: 0,
                    y1: 0,
                    colorStops: []
                };

                // get coordinates
                m2 = m1[2].match(/\w+/g);
                if(m2){
                    m2Len = m2.length;
                    for(i = 0; i < m2Len; i+=1){
                        switch(m2[i]) {
                            case 'top':
                                gradient.y1 = bounds.height;
                                break;

                            case 'right':
                                gradient.x0 = bounds.width;
                                break;

                            case 'bottom':
                                gradient.y0 = bounds.height;
                                break;

                            case 'left':
                                gradient.x1 = bounds.width;
                                break;
                        }
                    }
                }

                // get colors and stops
                m2 = m1[3].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\)(?:\s\d{1,3}%)?)+/g);
                if(m2){
                    m2Len = m2.length;
                    step = 1 / Math.max(m2Len - 1, 1);
                    for(i = 0; i < m2Len; i+=1){
                        m3 = m2[i].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\))\s*(\d{1,3})?(%)?/);
                        if(m3[2]){
                            stop = parseFloat(m3[2]);
                            if(m3[3]){ // percentage
                                stop /= 100;
                            }
                        } else {
                            stop = i * step;
                        }
                        gradient.colorStops.push({
                            color: m3[1],
                            stop: stop
                        });
                    }
                }
                break;

            case '-webkit-gradient':

                gradient = {
                    type: m1[2],
                    x0: 0,
                    y0: 0,
                    x1: 0,
                    y1: 0,
                    colorStops: []
                };

                // get coordinates
                m2 = m1[3].match(/(\d{1,3})%?\s(\d{1,3})%?,\s(\d{1,3})%?\s(\d{1,3})%?/);
                if(m2){
                    gradient.x0 = (m2[1] * bounds.width) / 100;
                    gradient.y0 = (m2[2] * bounds.height) / 100;
                    gradient.x1 = (m2[3] * bounds.width) / 100;
                    gradient.y1 = (m2[4] * bounds.height) / 100;
                }

                // get colors and stops
                m2 = m1[4].match(/((?:from|to|color-stop)\((?:[0-9\.]+,\s)?(?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\)\))+/g);
                if(m2){
                    m2Len = m2.length;
                    for(i = 0; i < m2Len; i+=1){
                        m3 = m2[i].match(/(from|to|color-stop)\(([0-9\.]+)?(?:,\s)?((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\))\)/);
                        stop = parseFloat(m3[2]);
                        if(m3[1] === 'from') stop = 0.0;
                        if(m3[1] === 'to') stop = 1.0;
                        gradient.colorStops.push({
                            color: m3[3],
                            stop: stop
                        });
                    }
                }
                break;
        }
    }

    return gradient;
};

_html2canvas.Generate.Gradient = function(src, bounds) {
    var canvas = document.createElement('canvas'),
    ctx = canvas.getContext('2d'),
    gradient, lingrad, i, len, img;

    canvas.width = bounds.width;
    canvas.height = bounds.height;

    gradient = _html2canvas.Generate.parseGradient(src, bounds);

    img = new Image();

    if(gradient && gradient.type === 'linear'){
        lingrad = ctx.createLinearGradient(gradient.x0, gradient.y0, gradient.x1, gradient.y1);

        for (i = 0, len = gradient.colorStops.length; i < len; i+=1) {
            try {
                lingrad.addColorStop(gradient.colorStops[i].stop, gradient.colorStops[i].color);
            }
            catch(e) {
                h2clog(['failed to add color stop: ', e, '; tried to add: ', gradient.colorStops[i], '; stop: ', i, '; in: ', src]);
            }
        }

        ctx.fillStyle = lingrad;
        ctx.fillRect(0, 0, bounds.width, bounds.height);

        img.src = canvas.toDataURL();
    }

    return img;
};

_html2canvas.Generate.ListAlpha = function(number) {
    var tmp = "",
    modulus;

    do {
        modulus = number % 26;
        tmp = String.fromCharCode((modulus) + 64) + tmp;
        number = number / 26;
    }while((number*26) > 26);

    return tmp;
};

_html2canvas.Generate.ListRoman = function(number) {
    var romanArray = ["M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"],
    decimal = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1],
    roman = "",
    v,
    len = romanArray.length;

    if (number <= 0 || number >= 4000) {
        return number;
    }

    for (v=0; v < len; v+=1) {
        while (number >= decimal[v]) {
            number -= decimal[v];
            roman += romanArray[v];
        }
    }

    return roman;

};

})();<commit_msg>"TODO: implement radial gradient generation"<commit_after>/*
  html2canvas @VERSION@ <http://html2canvas.hertzen.com>
  Copyright (c) 2011 Niklas von Hertzen. All rights reserved.
  http://www.twitter.com/niklasvh

  Released under MIT License
 */

(function(){

_html2canvas.Generate = {};

// -webkit-linear-gradient(left top, rgb(255, 0, 0), rgb(0, 0, 255), rgb(186, 218, 85), rgba(0, 0, 255, 0.496094))
// -webkit-linear-gradient(left, rgb(206, 219, 233) 0%, rgb(170, 197, 222) 17%, rgb(97, 153, 199) 50%, rgb(58, 132, 195) 51%, rgb(65, 154, 214) 59%, rgb(75, 184, 240) 71%, rgb(58, 139, 194) 84%, rgb(38, 85, 139) 100%)
// -webkit-gradient(linear, 0% 0, 0% 100%, from(rgb(252, 252, 252)), to(rgb(232, 232, 232)))
// -webkit-gradient(linear, 0% 0%, 0% 100%, from(rgb(240, 183, 161)), color-stop(0.5, rgb(140, 51, 16)), color-stop(0.51, rgb(117, 34, 1)), to(rgb(191, 110, 78)))
// -moz-linear-gradient(100% 0%, rgb(255, 0, 0), rgb(0, 0, 255), rgb(186, 218, 85), rgba(0, 0, 255, 0.5))

var reGradients = [
    /^(-webkit-linear-gradient)\(([a-z\s]+)((?:,\s(?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\)(?:\s\d{1,3}%)?)+)\)$/,
    /^(-webkit-gradient)\((linear|radial),\s((?:\d{1,3}%?)\s(?:\d{1,3}%?),\s(?:\d{1,3}%?)\s(?:\d{1,3}%?))((?:,\s(?:from|to|color-stop)\((?:[0-9\.]+,\s)?(?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\)\))+)\)$/
];

// ["-webkit-linear-gradient(left top, rgb(255, 0, 0), rgb(0, 0, 255), rgb(186, 218, 85), rgba(0, 0, 255, 0.496094))", "-webkit-linear-gradient", "left top", ", rgb(255, 0, 0), rgb(0, 0, 255), rgb(186, 218, 85), rgba(0, 0, 255, 0.496094)"]
// ["-webkit-linear-gradient(left, rgb(206, 219, 233) 0%, rgb(170, 197, 222) 17%, rgb(97, 153, 199) 50%, rgb(58, 132, 195) 51%, rgb(65, 154, 214) 59%, rgb(75, 184, 240) 71%, rgb(58, 139, 194) 84%, rgb(38, 85, 139) 100%)", "-webkit-linear-gradient", "left", ", rgb(206, 219, 233) 0%, rgb(170, 197, 222) 17%, rgb(97, 153, 199) 50%, rgb(58, 132, 195) 51%, rgb(65, 154, 214) 59%, rgb(75, 184, 240) 71%, rgb(58, 139, 194) 84%, rgb(38, 85, 139) 100%"]
// ["-webkit-gradient(linear, 0% 0, 0% 100%, from(rgb(252, 252, 252)), to(rgb(232, 232, 232)))", "-webkit-gradient", "linear", "0% 0", "0% 100%", ", from(rgb(252, 252, 252)), to(rgb(232, 232, 232))"]
// ["-webkit-gradient(linear, 0% 0%, 0% 100%, from(rgb(240, 183, 161)), color-stop(0.5, rgb(140, 51, 16)), color-stop(0.51, rgb(117, 34, 1)), to(rgb(191, 110, 78)))", "-webkit-gradient", "linear", "0% 0%, 0% 100%", ", from(rgb(240, 183, 161)), color-stop(0.5, rgb(140, 51, 16)), color-stop(0.51, rgb(117, 34, 1)), to(rgb(191, 110, 78))"]

_html2canvas.Generate.parseGradient = function(css, bounds) {
    var gradient, i, len = reGradients.length, m1, stop, m2, m2Len, step, m3;

    for(i = 0; i < len; i+=1){
        m1 = css.match(reGradients[i]);
        if(m1) break;
    }

    if(m1) {
        switch(m1[1]) {
            case '-webkit-linear-gradient':

                gradient = {
                    type: 'linear',
                    x0: 0,
                    y0: 0,
                    x1: 0,
                    y1: 0,
                    colorStops: []
                };

                // get coordinates
                m2 = m1[2].match(/\w+/g);
                if(m2){
                    m2Len = m2.length;
                    for(i = 0; i < m2Len; i+=1){
                        switch(m2[i]) {
                            case 'top':
                                gradient.y1 = bounds.height;
                                break;

                            case 'right':
                                gradient.x0 = bounds.width;
                                break;

                            case 'bottom':
                                gradient.y0 = bounds.height;
                                break;

                            case 'left':
                                gradient.x1 = bounds.width;
                                break;
                        }
                    }
                }

                // get colors and stops
                m2 = m1[3].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\)(?:\s\d{1,3}%)?)+/g);
                if(m2){
                    m2Len = m2.length;
                    step = 1 / Math.max(m2Len - 1, 1);
                    for(i = 0; i < m2Len; i+=1){
                        m3 = m2[i].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\))\s*(\d{1,3})?(%)?/);
                        if(m3[2]){
                            stop = parseFloat(m3[2]);
                            if(m3[3]){ // percentage
                                stop /= 100;
                            }
                        } else {
                            stop = i * step;
                        }
                        gradient.colorStops.push({
                            color: m3[1],
                            stop: stop
                        });
                    }
                }
                break;

            case '-webkit-gradient':

                gradient = {
                    type: m1[2],
                    x0: 0,
                    y0: 0,
                    x1: 0,
                    y1: 0,
                    colorStops: []
                };

                // get coordinates
                m2 = m1[3].match(/(\d{1,3})%?\s(\d{1,3})%?,\s(\d{1,3})%?\s(\d{1,3})%?/);
                if(m2){
                    gradient.x0 = (m2[1] * bounds.width) / 100;
                    gradient.y0 = (m2[2] * bounds.height) / 100;
                    gradient.x1 = (m2[3] * bounds.width) / 100;
                    gradient.y1 = (m2[4] * bounds.height) / 100;
                }

                // get colors and stops
                m2 = m1[4].match(/((?:from|to|color-stop)\((?:[0-9\.]+,\s)?(?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\)\))+/g);
                if(m2){
                    m2Len = m2.length;
                    for(i = 0; i < m2Len; i+=1){
                        m3 = m2[i].match(/(from|to|color-stop)\(([0-9\.]+)?(?:,\s)?((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\))\)/);
                        stop = parseFloat(m3[2]);
                        if(m3[1] === 'from') stop = 0.0;
                        if(m3[1] === 'to') stop = 1.0;
                        gradient.colorStops.push({
                            color: m3[3],
                            stop: stop
                        });
                    }
                }
                break;
        }
    }

    return gradient;
};

_html2canvas.Generate.Gradient = function(src, bounds) {
    var canvas = document.createElement('canvas'),
    ctx = canvas.getContext('2d'),
    gradient, lingrad, i, len, img;

    canvas.width = bounds.width;
    canvas.height = bounds.height;

    gradient = _html2canvas.Generate.parseGradient(src, bounds);

    img = new Image();

    if(gradient && gradient.type === 'linear'){
        lingrad = ctx.createLinearGradient(gradient.x0, gradient.y0, gradient.x1, gradient.y1);

        for (i = 0, len = gradient.colorStops.length; i < len; i+=1) {
            try {
                lingrad.addColorStop(gradient.colorStops[i].stop, gradient.colorStops[i].color);
            }
            catch(e) {
                h2clog(['failed to add color stop: ', e, '; tried to add: ', gradient.colorStops[i], '; stop: ', i, '; in: ', src]);
            }
        }

        ctx.fillStyle = lingrad;
        ctx.fillRect(0, 0, bounds.width, bounds.height);

        img.src = canvas.toDataURL();
    } else if(gradient && gradient.type === 'radial'){
        // TODO: implement radial gradient generation
        h2clog('No radial gradient support implemented');
    }

    return img;
};

_html2canvas.Generate.ListAlpha = function(number) {
    var tmp = "",
    modulus;

    do {
        modulus = number % 26;
        tmp = String.fromCharCode((modulus) + 64) + tmp;
        number = number / 26;
    }while((number*26) > 26);

    return tmp;
};

_html2canvas.Generate.ListRoman = function(number) {
    var romanArray = ["M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"],
    decimal = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1],
    roman = "",
    v,
    len = romanArray.length;

    if (number <= 0 || number >= 4000) {
        return number;
    }

    for (v=0; v < len; v+=1) {
        while (number >= decimal[v]) {
            number -= decimal[v];
            roman += romanArray[v];
        }
    }

    return roman;

};

})();<|endoftext|>


<commit_before>import React, {PureComponent, PropTypes} from 'react';
import KeyboardButton from './KeyboardButton';

import {LatinLayout, CyrillicLayout} from './layouts';
import SymbolsLayout from './layouts/SymbolsLayout';

import BackspaceIcon from './icons/BackspaceIcon';
import LanguageIcon from './icons/LanguageIcon';
import ShiftIcon from './icons/ShiftIcon';

export default class Keyboard extends PureComponent {
	static propTypes = {
		leftButtons: PropTypes.arrayOf(PropTypes.node),
		rightButtons: PropTypes.arrayOf(PropTypes.node),
		inputNode: PropTypes.any.isRequired,
		onClick: PropTypes.func,
		isFirstLetterUppercase: PropTypes.bool,
		defaultLanguage: PropTypes.string,
	};

	static defaultProps = {
		leftButtons: [],
		rightButtons: [],
		isFirstLetterUppercase: false,
		defaultLanguage: 'cyrrilic',
	};

	constructor(props) {
		super(props);
		this.handleLetterButtonClick = this.handleLetterButtonClick.bind(this);
		this.handleBackspaceClick = this.handleBackspaceClick.bind(this);
		this.handleLanguageClick = this.handleLanguageClick.bind(this);
		this.handleShiftClick = this.handleShiftClick.bind(this);
		this.handleSymbolsClick = this.handleSymbolsClick.bind(this);

		this.state = {
			currentLanguage: 0,
			languages: props.languages ==null ? [LatinLayout,CyrillicLayout] : this.props.languages,
			showSymbols: false,
			uppercase: this.isUppercase(),
		};
	}

	handleLanguageClick() {
		this.setState(
			{
				currentLanguage: (this.state.currentLanguage + 1)%this.state.languages.length
			}
		);
	}

	handleShiftClick() {
		this.setState({uppercase: !this.state.uppercase});
	}

	handleSymbolsClick() {
		this.setState({showSymbols: !this.state.showSymbols});
	}

	handleLetterButtonClick(key) {
		const {inputNode} = this.props;
		const {value, selectionStart, selectionEnd} = inputNode;
		const nextValue = value.substring(0, selectionStart) + key + value.substring(selectionEnd);

		inputNode.value = nextValue;
		if (this.props.onClick) {
			this.props.onClick(nextValue);
		}
		setTimeout(() => {
			inputNode.focus();
			inputNode.setSelectionRange(selectionStart + 1, selectionStart + 1);
		}, 0);
		this.setState({uppercase: this.isUppercase()});
		inputNode.dispatchEvent(new Event('input'));
	}

	isUppercase() {
		const {inputNode, isFirstLetterUppercase} = this.props;
		return inputNode.type !== 'password' &&
			inputNode.dataset.type !== 'email' &&
			!inputNode.value.length && isFirstLetterUppercase;
	}

	handleBackspaceClick() {
		const {inputNode} = this.props;
		const {value, selectionStart, selectionEnd} = inputNode;
		let nextValue;
		let nextSelectionPosition;
		if (selectionStart === selectionEnd) {
			nextValue = value.substring(0, selectionStart - 1) + value.substring(selectionEnd);
			nextSelectionPosition = selectionStart - 1;
		} else {
			nextValue = value.substring(0, selectionStart) + value.substring(selectionEnd);
			nextSelectionPosition = selectionStart;
		}
		nextSelectionPosition = (nextSelectionPosition > 0) ? nextSelectionPosition : 0;

		inputNode.value = nextValue;
		if (this.props.onClick) {
			this.props.onClick(nextValue);
		}
		setTimeout(() => {
			inputNode.focus();
			inputNode.setSelectionRange(nextSelectionPosition, nextSelectionPosition);
		}, 0);
		this.setState({uppercase: this.isUppercase()});
		inputNode.dispatchEvent(new Event('input'));
	}

	getKeys() {
		let keysSet;
		if (this.state.showSymbols) {
			keysSet = SymbolsLayout.layout;
		} else {
			keysSet = this.state.languages[this.state.currentLanguage].layout;
		}

		return this.state.uppercase ?
			keysSet.map(keyRow => keyRow.map(key => key.toUpperCase()))
			: keysSet;
	}

	getSymbolsKeyValue() {
		if (this.state.showSymbols) {
			return this.state.languages[this.state.currentLanguage].symbolsKeyValue;
		} else {
			return SymbolsLayout.symbolsKeyValue;
		}
	}

	render() {
		const {leftButtons, rightButtons, inputNode} = this.props;
		const keys = this.getKeys();
		const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0];
		const symbolsKeyValue = this.getSymbolsKeyValue();

		return (

					{numbers.map((button) =>
						<KeyboardButton
							value={button}
							onClick={this.handleLetterButtonClick}
							classes={"keyboard-numberButton"}
							key={button}
						/>
					)}
					<KeyboardButton
						value={<BackspaceIcon />}
						onClick={this.handleBackspaceClick}
					/>

					{keys[0].map((button) =>
						<KeyboardButton
							value={button}
							onClick={this.handleLetterButtonClick}
							key={button}
						/>
					)}

					{keys[1].map((button) =>
						<KeyboardButton
							value={button}
							onClick={this.handleLetterButtonClick}
							key={button}
						/>
					)}

					<KeyboardButton
						value={<ShiftIcon />}
						onClick={this.handleShiftClick}
					/>
					{keys[2].map((button) =>
						<KeyboardButton
							value={button}
							onClick={this.handleLetterButtonClick}
							key={button}
						/>
					)}
					<KeyboardButton
						value={symbolsKeyValue}
						onClick={this.handleSymbolsClick}
					/>

					{leftButtons}
					{this.state.languages.length > 1 ?
						<KeyboardButton
							value={<LanguageIcon />}
							onClick={this.handleLanguageClick}
						/>
					:null}
					{inputNode.dataset.type === 'email' ?
						<KeyboardButton
							value={'@'}
							onClick={this.handleLetterButtonClick}
						/>
					: null}
					<KeyboardButton
						value={' '}
						classes="keyboard-space"
						onClick={this.handleLetterButtonClick}
					/>
					{inputNode.dataset.type === 'email' ?
						<KeyboardButton
							value={'.'}
							onClick={this.handleLetterButtonClick}
						/>
					: null}
					{rightButtons}

		);
	}
}
<commit_msg>moving languages to props<commit_after>import React, {PureComponent, PropTypes} from 'react';
import KeyboardButton from './KeyboardButton';

import {LatinLayout, CyrillicLayout} from './layouts';
import SymbolsLayout from './layouts/SymbolsLayout';

import BackspaceIcon from './icons/BackspaceIcon';
import LanguageIcon from './icons/LanguageIcon';
import ShiftIcon from './icons/ShiftIcon';

export default class Keyboard extends PureComponent {
	static propTypes = {
		leftButtons: PropTypes.arrayOf(PropTypes.node),
		rightButtons: PropTypes.arrayOf(PropTypes.node),
		inputNode: PropTypes.any.isRequired,
		onClick: PropTypes.func,
		isFirstLetterUppercase: PropTypes.bool,
		defaultLanguage: PropTypes.string,
	};

	static defaultProps = {
		leftButtons: [],
		rightButtons: [],
		isFirstLetterUppercase: false,
		languages: [LatinLayout, CyrillicLayout],
	};

	constructor(props) {
		super(props);
		this.handleLetterButtonClick = this.handleLetterButtonClick.bind(this);
		this.handleBackspaceClick = this.handleBackspaceClick.bind(this);
		this.handleLanguageClick = this.handleLanguageClick.bind(this);
		this.handleShiftClick = this.handleShiftClick.bind(this);
		this.handleSymbolsClick = this.handleSymbolsClick.bind(this);

		this.state = {
			currentLanguage: 0,

			showSymbols: false,
			uppercase: this.isUppercase(),
		};
	}

	handleLanguageClick() {
		this.setState(
			{
				currentLanguage: (this.state.currentLanguage + 1) % this.props.languages.length
			}
		);
	}

	handleShiftClick() {
		this.setState({uppercase: !this.state.uppercase});
	}

	handleSymbolsClick() {
		this.setState({showSymbols: !this.state.showSymbols});
	}

	handleLetterButtonClick(key) {
		const {inputNode} = this.props;
		const {value, selectionStart, selectionEnd} = inputNode;
		const nextValue = value.substring(0, selectionStart) + key + value.substring(selectionEnd);

		inputNode.value = nextValue;
		if (this.props.onClick) {
			this.props.onClick(nextValue);
		}
		setTimeout(() => {
			inputNode.focus();
			inputNode.setSelectionRange(selectionStart + 1, selectionStart + 1);
		}, 0);
		this.setState({uppercase: this.isUppercase()});
		inputNode.dispatchEvent(new Event('input'));
	}

	isUppercase() {
		const {inputNode, isFirstLetterUppercase} = this.props;
		return inputNode.type !== 'password' &&
			inputNode.dataset.type !== 'email' &&
			!inputNode.value.length && isFirstLetterUppercase;
	}

	handleBackspaceClick() {
		const {inputNode} = this.props;
		const {value, selectionStart, selectionEnd} = inputNode;
		let nextValue;
		let nextSelectionPosition;
		if (selectionStart === selectionEnd) {
			nextValue = value.substring(0, selectionStart - 1) + value.substring(selectionEnd);
			nextSelectionPosition = selectionStart - 1;
		} else {
			nextValue = value.substring(0, selectionStart) + value.substring(selectionEnd);
			nextSelectionPosition = selectionStart;
		}
		nextSelectionPosition = (nextSelectionPosition > 0) ? nextSelectionPosition : 0;

		inputNode.value = nextValue;
		if (this.props.onClick) {
			this.props.onClick(nextValue);
		}
		setTimeout(() => {
			inputNode.focus();
			inputNode.setSelectionRange(nextSelectionPosition, nextSelectionPosition);
		}, 0);
		this.setState({uppercase: this.isUppercase()});
		inputNode.dispatchEvent(new Event('input'));
	}

	getKeys() {
		let keysSet;
		if (this.state.showSymbols) {
			keysSet = SymbolsLayout.layout;
		} else {
			keysSet = this.props.languages[this.state.currentLanguage].layout;
		}

		return this.state.uppercase ?
			keysSet.map(keyRow => keyRow.map(key => key.toUpperCase()))
			: keysSet;
	}

	getSymbolsKeyValue() {
		if (this.state.showSymbols) {
			return this.props.languages[this.state.currentLanguage].symbolsKeyValue;
		}
		return SymbolsLayout.symbolsKeyValue;
	}

	render() {
		const {leftButtons, rightButtons, inputNode} = this.props;
		const keys = this.getKeys();
		const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0];
		const symbolsKeyValue = this.getSymbolsKeyValue();

		return (

					{numbers.map(button =>
						<KeyboardButton
							value={button}
							onClick={this.handleLetterButtonClick}
							classes={'keyboard-numberButton'}
							key={button}
						/>
					)}
					<KeyboardButton
						value={<BackspaceIcon />}
						onClick={this.handleBackspaceClick}
					/>

					{keys[0].map(button =>
						<KeyboardButton
							value={button}
							onClick={this.handleLetterButtonClick}
							key={button}
						/>
					)}

					{keys[1].map(button =>
						<KeyboardButton
							value={button}
							onClick={this.handleLetterButtonClick}
							key={button}
						/>
					)}

					<KeyboardButton
						value={<ShiftIcon />}
						onClick={this.handleShiftClick}
					/>
					{keys[2].map(button =>
						<KeyboardButton
							value={button}
							onClick={this.handleLetterButtonClick}
							key={button}
						/>
					)}
					<KeyboardButton
						value={symbolsKeyValue}
						onClick={this.handleSymbolsClick}
					/>

					{leftButtons}
					{this.props.languages.length > 1 ?
						<KeyboardButton
							value={<LanguageIcon />}
							onClick={this.handleLanguageClick}
						/>
					: null}
					{inputNode.dataset.type === 'email' ?
						<KeyboardButton
							value={'@'}
							onClick={this.handleLetterButtonClick}
						/>
					: null}
					<KeyboardButton
						value={' '}
						classes="keyboard-space"
						onClick={this.handleLetterButtonClick}
					/>
					{inputNode.dataset.type === 'email' ?
						<KeyboardButton
							value={'.'}
							onClick={this.handleLetterButtonClick}
						/>
					: null}
					{rightButtons}

		);
	}
}
<|endoftext|>


<commit_before>(function (root, factory) {
    if (typeof define === 'function' && define.amd) {
        define([], factory);
    } else {
        root.probabilitydrive = factory();
    }
}(this, function () {

    function ProbabilityDrive() {
        this.currentUrl;
        this.store           = {};
        this.routeUrls       = [];
        this.blacklistUrls   = [];
        this.countThreshold  = 0;
    }

    ProbabilityDrive.prototype.observe = function(url) {
        if (!this.currentUrl || this.currentUrl === url) {
            this.currentUrl = url;
            return this;
        }

        incrementUrl.call(this, url);
        analyse.call(this, this.currentUrl);

        this.currentUrl = url;

        return this;
    }

    ProbabilityDrive.prototype.determine = function() {
        url = this.currentUrl;

        var minCount = 0;
        var result = [];

        iterateStore.call(this, url, function(urlData) {
            if (urlData.count < this.countThreshold) {
                return;
            }

            if (minCount <= urlData.count) {
                result.push(urlData.url);
                minCount = urlData.count
            } else {
                return false;
            }
        });
        return result;
    }

    ProbabilityDrive.prototype.percentile = function(percentile) {
        percentile = percentile / 100;

        var data = this.store[this.currentUrl];
        if (!data) {
            return [];
        }

        var first = data[0];

        var multiplier = 1 / first.probability;

        var results = [];
        results.push(first.url);

        iterateStore.call(this, url, function(urlData, i) {
            if (i == 0) {
                return;
            }

            if (urlData.count < this.countThreshold) {
                return;
            }

            if (urlData.probability * multiplier >= percentile) {
                results.push(urlData.url);
            } else {
                return false;
            }
        });
        return results;
    }

    ProbabilityDrive.prototype.probability = function(probability) {
        var data = this.store[this.currentUrl];
        var result = [];

        iterateStore.call(this, url, function(urlData, i) {
            if (urlData.count < this.countThreshold) {
                return;
            }

            if (urlData.probability >= probability) {
                result.push(urlData.url);
            } else {
                return false;
            }
        });
        return result;
    }

    ProbabilityDrive.prototype.setCountThreshold = function(threshold) {
        this.countThreshold = threshold;
    }

    ProbabilityDrive.prototype.routes = function(routes) {
        for (var i = 0; i < routes.length; i++) {
            this.routeUrls.push(
                stripAndBreakUrl(routes[i], '/')
            )
        }
    }

    ProbabilityDrive.prototype.blacklist = function(routes) {
        for (var i = 0; i < routes.length; i++) {
            this.blacklistUrls.push(
                stripAndBreakUrl(routes[i], '/')
            )
        }
    }

    ProbabilityDrive.prototype.getData = function() {
        return {
            currentUrl: this.currentUrl,
            store: this.store
        };
    }

    ProbabilityDrive.prototype.setData = function(data) {
        data = data || {};
        this.currentUrl = data.currentUrl;
        this.store = data.store || {};
    }

    // Aliases
    ProbabilityDrive.prototype.here =
        ProbabilityDrive.prototype.observe

    /**
     * Convenience method to iterate through the store under a URL
     *
     * Returning false from the callback will terminate the loop early.
     */
    function iterateStore(url, callback) {
        for (var i in this.store[url]) {
            var urlData = this.store[url][i];
            if (false === callback.call(this, urlData, i)) {
                return;
            }
        }
    }

    function incrementUrl(url) {
        if (matchRoute(url, this.blacklistUrls)) {
            // Route is blacklisted
            return;
        }

        var matchResult = matchRoute(url, this.routeUrls);
        if (matchResult) {
            // Use parameterised route
            url = matchResult;
        }

        this.store[this.currentUrl] = this.store[this.currentUrl] || [];

        var found = false;
        for (var i in this.store[this.currentUrl]) {
            if (this.store[this.currentUrl][i].url === url) {
                found = true;
                this.store[this.currentUrl][i].count += 1;
                break;
            }
        }

        if (!found) {
            this.store[this.currentUrl].push({
                url:   url,
                count: 1,
                probability: 0
            });
        }
    }

    function analyse(url) {
        if (!this.store[url]) {
            return;
        }

        var total = this.store[url].reduce(function(total, urlData) {
            return total += urlData.count;
        }, 0);

        this.store[url].forEach(function(urlData) {
            urlData.probability = urlData.count / total;
        });

        this.store[url].sort(function(a, b) {
            return a.count < b.count;
        });
    }

    function matchRoute(url, list) {
        var urlParts = stripAndBreakUrl(url, '/');

        for (var i = 0; i < list.length; i++) {
            var routeUrlParts = list[i];
            if (isMatchRouteParts(urlParts, routeUrlParts)) {
                return '/' + routeUrlParts.join('/');
            }
        }

        return false;
    }

    function isMatchRouteParts(urlParts, routeUrlParts) {
        for (var i = 0; i < routeUrlParts.length; i++) {
            if (!urlParts[i] ||
                (routeUrlParts[i] !== urlParts[i] &&
                routeUrlParts[i].indexOf(':') === -1)
            ) {
                break;
            }

            return true;
        }
        return false;
    }

    function stripAndBreakUrl(string, character) {
        string = string.charAt(0) !== character ? string : string.substring(1);
        return string.split('/');
    }

    return ProbabilityDrive;

}));
<commit_msg>Make iterateStore helper deal with thresholdCount<commit_after>(function (root, factory) {
    if (typeof define === 'function' && define.amd) {
        define([], factory);
    } else {
        root.probabilitydrive = factory();
    }
}(this, function () {

    function ProbabilityDrive() {
        this.currentUrl;
        this.store           = {};
        this.routeUrls       = [];
        this.blacklistUrls   = [];
        this.countThreshold  = 0;
    }

    ProbabilityDrive.prototype.observe = function(url) {
        if (!this.currentUrl || this.currentUrl === url) {
            this.currentUrl = url;
            return this;
        }

        incrementUrl.call(this, url);
        analyse.call(this, this.currentUrl);

        this.currentUrl = url;

        return this;
    }

    ProbabilityDrive.prototype.determine = function() {
        url = this.currentUrl;

        var minCount = 0;
        var result = [];

        iterateStore.call(this, url, function(urlData) {
            if (minCount > urlData.count) {
                return false;
            }
            result.push(urlData.url);
            minCount = urlData.count
        });
        return result;
    }

    ProbabilityDrive.prototype.percentile = function(percentile) {
        percentile = percentile / 100;

        var data = this.store[this.currentUrl];
        if (!data) {
            return [];
        }

        var first = data[0];

        var multiplier = 1 / first.probability;

        var results = [];
        results.push(first.url);

        iterateStore.call(this, url, function(urlData, i) {
            if (i == 0) {
                return;
            }

            if (urlData.probability * multiplier < percentile) {
                return false;
            }

            results.push(urlData.url);
        });
        return results;
    }

    ProbabilityDrive.prototype.probability = function(probability) {
        var data = this.store[this.currentUrl];
        var result = [];

        iterateStore.call(this, url, function(urlData, i) {
            if (urlData.probability < probability) {
                return false;
            }
            result.push(urlData.url);
        });
        return result;
    }

    ProbabilityDrive.prototype.setCountThreshold = function(threshold) {
        this.countThreshold = threshold;
    }

    ProbabilityDrive.prototype.routes = function(routes) {
        for (var i = 0; i < routes.length; i++) {
            this.routeUrls.push(
                stripAndBreakUrl(routes[i], '/')
            )
        }
    }

    ProbabilityDrive.prototype.blacklist = function(routes) {
        for (var i = 0; i < routes.length; i++) {
            this.blacklistUrls.push(
                stripAndBreakUrl(routes[i], '/')
            )
        }
    }

    ProbabilityDrive.prototype.getData = function() {
        return {
            currentUrl: this.currentUrl,
            store: this.store
        };
    }

    ProbabilityDrive.prototype.setData = function(data) {
        data = data || {};
        this.currentUrl = data.currentUrl;
        this.store = data.store || {};
    }

    // Aliases
    ProbabilityDrive.prototype.here =
        ProbabilityDrive.prototype.observe

    /**
     * Convenience method to iterate through the store under a URL
     *
     * Will always respect countThreshold
     * Returning false from the callback will terminate the loop early.
     */
    function iterateStore(url, callback) {
        for (var i in this.store[url]) {
            var urlData = this.store[url][i];

            if (urlData.count < this.countThreshold) {
                continue;
            }

            if (false === callback.call(this, urlData, i)) {
                return;
            }
        }
    }

    function incrementUrl(url) {
        if (matchRoute(url, this.blacklistUrls)) {
            // Route is blacklisted
            return;
        }

        var matchResult = matchRoute(url, this.routeUrls);
        if (matchResult) {
            // Use parameterised route
            url = matchResult;
        }

        this.store[this.currentUrl] = this.store[this.currentUrl] || [];

        var found = false;
        for (var i in this.store[this.currentUrl]) {
            if (this.store[this.currentUrl][i].url === url) {
                found = true;
                this.store[this.currentUrl][i].count += 1;
                break;
            }
        }

        if (!found) {
            this.store[this.currentUrl].push({
                url:   url,
                count: 1,
                probability: 0
            });
        }
    }

    function analyse(url) {
        if (!this.store[url]) {
            return;
        }

        var total = this.store[url].reduce(function(total, urlData) {
            return total += urlData.count;
        }, 0);

        this.store[url].forEach(function(urlData) {
            urlData.probability = urlData.count / total;
        });

        this.store[url].sort(function(a, b) {
            return a.count < b.count;
        });
    }

    function matchRoute(url, list) {
        var urlParts = stripAndBreakUrl(url, '/');

        for (var i = 0; i < list.length; i++) {
            var routeUrlParts = list[i];
            if (isMatchRouteParts(urlParts, routeUrlParts)) {
                return '/' + routeUrlParts.join('/');
            }
        }

        return false;
    }

    function isMatchRouteParts(urlParts, routeUrlParts) {
        for (var i = 0; i < routeUrlParts.length; i++) {
            if (!urlParts[i] ||
                (routeUrlParts[i] !== urlParts[i] &&
                routeUrlParts[i].indexOf(':') === -1)
            ) {
                break;
            }

            return true;
        }
        return false;
    }

    function stripAndBreakUrl(string, character) {
        string = string.charAt(0) !== character ? string : string.substring(1);
        return string.split('/');
    }

    return ProbabilityDrive;

}));
<|endoftext|>


<commit_before>const h = require('../h')
const fs = require('fs')
const { Value, when, computed } = require('@mmckegg/mutant')
const u = require('../util')
const pull = require('pull-stream')
const Scroller = require('pull-scroll')
const TextNodeSearcher = require('text-node-searcher')

exports.needs = {
  build_scroller: 'first',
  message_render: 'first',
  sbot_log: 'first',
  sbot_fulltext_search: 'first'
}

exports.gives = {
  screen_view: true,
  mcss: true
}

var whitespace = /\s+/

function andSearch(terms, inputs) {
  for(var i = 0; i < terms.length; i++) {
    var match = false
    for(var j = 0; j < inputs.length; j++) {
      if(terms[i].test(inputs[j])) match = true
    }
    //if a term was not matched by anything, filter this one
    if(!match) return false
  }
  return true
}

function searchFilter(terms) {
  return function (msg) {
    var c = msg && msg.value && msg.value.content
    return c && (
      msg.key == terms[0] ||
      andSearch(terms.map(function (term) {
        return new RegExp('\\b'+term+'\\b', 'i')
      }), [c.text, c.name, c.title])
    )
  }
}

function createOrRegExp(ary) {
  return new RegExp(ary.map(function (e) {
    return '\\b'+e+'\\b'
  }).join('|'), 'i')
}

function highlight(el, query) {
  var searcher = new TextNodeSearcher({container: el})
  searcher.query = query
  searcher.highlight()
  return el
}

function fallback(reader) {
  var fallbackRead
  return function (read) {
    return function (abort, cb) {
      read(abort, function next(end, data) {
        if (end && reader && (fallbackRead = reader(end))) {
          reader = null
          read = fallbackRead
          read(abort, next)
        } else {
          cb(end, data)
        }
      })
    }
  }
}

exports.create = function (api) {

  return {
    screen_view,
    mcss: () => fs.readFileSync(__filename.replace(/js$/, 'mcss'), 'utf8')
  }

  function screen_view (path) {
    if (path[0] !== '?') return

    var queryStr = path.substr(1).trim()
    var query = queryStr.split(whitespace)
    var matchesQuery = searchFilter(query)

    const isLinearSearch = Value(false)
    const isFulltextSearchDone = Value(false)
    const searched = Value(0)
    const matches = Value(0)
    const hasNoFulltextMatches = computed([isFulltextSearchDone, matches],
      (done, matches) => done && matches === 0)
    const searchHeader = h('Search', [
      h('header', h('h1', query.join(' '))),
      when(isLinearSearch,
        h('section.details', [
          h('div.searched', ['Searched: ', searched]),
          h('div.matches', [matches, ' matches'])
        ]),
        when(hasNoFulltextMatches,
          h('section.details', [
            h('div.matches', 'No matches')
          ])
        )
      )
    ])
    var { container, content } = api.build_scroller({ prepend: searchHeader })
    container.id = path // helps tabs find this tab

    function renderMsg(msg) {
      var el = api.message_render(msg)
      highlight(el, createOrRegExp(query))
      return el
    }

    pull(
      api.sbot_log({old: false}),
      pull.filter(matchesQuery),
      Scroller(container, content, renderMsg, true, false)
    )

    pull(
      u.next(api.sbot_fulltext_search, {query: queryStr, reverse: true, limit: 500, live: false}),
      fallback((err) => {
        if (err === true) {
          isFulltextSearchDone.set(true)
        } else if (/^no source/.test(err.message)) {
          isLinearSearch.set(true)
          return pull(
            u.next(api.sbot_log, {reverse: true, limit: 500, live: false}),
            pull.through(() => searched.set(searched()+1)),
            pull.filter(matchesQuery)
          )
        }
      }),
      pull.through(() => matches.set(matches()+1)),
      Scroller(container, content, renderMsg, false, false)
    )

    return container
  }
}

<commit_msg>collect search state into struct<commit_after>const h = require('../h')
const fs = require('fs')
const { Struct, Value, when, computed } = require('@mmckegg/mutant')
const u = require('../util')
const pull = require('pull-stream')
const Scroller = require('pull-scroll')
const TextNodeSearcher = require('text-node-searcher')

exports.needs = {
  build_scroller: 'first',
  message_render: 'first',
  sbot_log: 'first',
  sbot_fulltext_search: 'first'
}

exports.gives = {
  screen_view: true,
  mcss: true
}

var whitespace = /\s+/

function andSearch(terms, inputs) {
  for(var i = 0; i < terms.length; i++) {
    var match = false
    for(var j = 0; j < inputs.length; j++) {
      if(terms[i].test(inputs[j])) match = true
    }
    //if a term was not matched by anything, filter this one
    if(!match) return false
  }
  return true
}

function searchFilter(terms) {
  return function (msg) {
    var c = msg && msg.value && msg.value.content
    return c && (
      msg.key == terms[0] ||
      andSearch(terms.map(function (term) {
        return new RegExp('\\b'+term+'\\b', 'i')
      }), [c.text, c.name, c.title])
    )
  }
}

function createOrRegExp(ary) {
  return new RegExp(ary.map(function (e) {
    return '\\b'+e+'\\b'
  }).join('|'), 'i')
}

function highlight(el, query) {
  var searcher = new TextNodeSearcher({container: el})
  searcher.query = query
  searcher.highlight()
  return el
}

function fallback(reader) {
  var fallbackRead
  return function (read) {
    return function (abort, cb) {
      read(abort, function next(end, data) {
        if (end && reader && (fallbackRead = reader(end))) {
          reader = null
          read = fallbackRead
          read(abort, next)
        } else {
          cb(end, data)
        }
      })
    }
  }
}

exports.create = function (api) {

  return {
    screen_view,
    mcss: () => fs.readFileSync(__filename.replace(/js$/, 'mcss'), 'utf8')
  }

  function screen_view (path) {
    if (path[0] !== '?') return

    var queryStr = path.substr(1).trim()
    var query = queryStr.split(whitespace)
    var matchesQuery = searchFilter(query)

    const search = Struct({
      isLinear: Value(false),
      linear: Struct({
        checked: Value(0)
      }),
      fulltext: Struct({
        isDone: Value(false)
      }),
      matches: Value(0)
    })
    const hasNoFulltextMatches = computed([search.fulltext.isDone, search.matches],
      (done, matches) => done && matches === 0)

    const searchHeader = h('Search', [
      h('header', h('h1', query.join(' '))),
      when(search.isLinear,
        h('section.details', [
          h('div.searched', ['Searched: ', search.linear.checked]),
          h('div.matches', [search.matches, ' matches'])
        ]),
        h('section.details', [
          h('div.searched'),
          when(hasNoFulltextMatches, h('div.matches', 'No matches'))
        ])
      )
    ])
    var { container, content } = api.build_scroller({ prepend: searchHeader })
    container.id = path // helps tabs find this tab

    function renderMsg(msg) {
      var el = api.message_render(msg)
      highlight(el, createOrRegExp(query))
      return el
    }

    pull(
      api.sbot_log({old: false}),
      pull.filter(matchesQuery),
      Scroller(container, content, renderMsg, true, false)
    )

    pull(
      u.next(api.sbot_fulltext_search, {query: queryStr, reverse: true, limit: 500, live: false}),
      fallback((err) => {
        if (err === true) {
          search.fulltext.isDone.set(true)
        } else if (/^no source/.test(err.message)) {
          search.isLinear.set(true)
          return pull(
            u.next(api.sbot_log, {reverse: true, limit: 500, live: false}),
            pull.through(() => searched.set(searched()+1)),
            pull.filter(matchesQuery)
          )
        }
      }),
      pull.through(() => search.matches.set(search.matches()+1)),
      Scroller(container, content, renderMsg, false, false)
    )

    return container
  }
}

<|endoftext|>


<commit_before>/*
Copyright 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 commonmark from 'commonmark';
import escape from 'lodash/escape';

const ALLOWED_HTML_TAGS = ['del'];

// These types of node are definitely text
const TEXT_NODES = ['text', 'softbreak', 'linebreak', 'paragraph', 'document'];

function is_allowed_html_tag(node) {
    // Regex won't work for tags with attrs, but we only
    // allow <del> anyway.
    const matches = /^<\/?(.*)>$/.exec(node.literal);
    if (matches && matches.length == 2) {
        const tag = matches[1];
        return ALLOWED_HTML_TAGS.indexOf(tag) > -1;
    }
    return false;
}

function html_if_tag_allowed(node) {
    if (is_allowed_html_tag(node)) {
        this.lit(node.literal);
        return;
    } else {
        this.lit(escape(node.literal));
    }
}

/*
 * Returns true if the parse output containing the node
 * comprises multiple block level elements (ie. lines),
 * or false if it is only a single line.
 */
function is_multi_line(node) {
    var par = node;
    while (par.parent) {
        par = par.parent;
    }
    return par.firstChild != par.lastChild;
}

/**
 * Class that wraps commonmark, adding the ability to see whether
 * a given message actually uses any markdown syntax or whether
 * it's plain text.
 */
export default class Markdown {
    constructor(input) {
        this.input = input;

        const parser = new commonmark.Parser();
        this.parsed = parser.parse(this.input);
    }

    isPlainText() {
        const walker = this.parsed.walker();

        let ev;
        while ( (ev = walker.next()) ) {
            const node = ev.node;
            if (TEXT_NODES.indexOf(node.type) > -1) {
                // definitely text
                continue;
            } else if (node.type == 'html_inline' || node.type == 'html_block') {
                // if it's an allowed html tag, we need to render it and therefore
                // we will need to use HTML. If it's not allowed, it's not HTML since
                // we'll just be treating it as text.
                if (is_allowed_html_tag(node)) {
                    return false;
                }
            } else {
                return false;
            }
        }
        return true;
    }

    toHTML() {
        const renderer = new commonmark.HtmlRenderer({safe: false});
        const real_paragraph = renderer.paragraph;

        renderer.paragraph = function(node, entering) {
            // If there is only one top level node, just return the
            // bare text: it's a single line of text and so should be
            // 'inline', rather than unnecessarily wrapped in its own
            // p tag. If, however, we have multiple nodes, each gets
            // its own p tag to keep them as separate paragraphs.
            if (is_multi_line(node)) {
                real_paragraph.call(this, node, entering);
            }
        };

        renderer.html_inline = html_if_tag_allowed;
        renderer.html_block = function(node) {
            // as with `paragraph`, we only insert line breaks
            // if there are multiple lines in the markdown.
            const isMultiLine = is_multi_line(node);

            if (isMultiLine) this.cr();
            html_if_tag_allowed.call(this, node);
            if (isMultiLine) this.cr();
        }

        return renderer.render(this.parsed);
    }

    /*
     * Render the markdown message to plain text. That is, essentially
     * just remove any backslashes escaping what would otherwise be
     * markdown syntax
     * (to fix https://github.com/vector-im/riot-web/issues/2870)
     */
    toPlaintext() {
        const renderer = new commonmark.HtmlRenderer({safe: false});
        const real_paragraph = renderer.paragraph;

        // The default `out` function only sends the input through an XML
        // escaping function, which causes messages to be entity encoded,
        // which we don't want in this case.
        renderer.out = function(s) {
            // The `lit` function adds a string literal to the output buffer.
            this.lit(s);
        };

        renderer.paragraph = function(node, entering) {
            // as with toHTML, only append lines to paragraphs if there are
            // multiple paragraphs
            if (is_multi_line(node)) {
                if (!entering && node.next) {
                    this.lit('\n\n');
                }
            }
        };
        renderer.html_block = function(node) {
            this.lit(node.literal);
            if (is_multi_line(node) && node.next) this.lit('\n\n');
        }

        return renderer.render(this.parsed);
    }
}
<commit_msg>Fix block quotes all being on a single line<commit_after>/*
Copyright 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 commonmark from 'commonmark';
import escape from 'lodash/escape';

const ALLOWED_HTML_TAGS = ['del'];

// These types of node are definitely text
const TEXT_NODES = ['text', 'softbreak', 'linebreak', 'paragraph', 'document'];

function is_allowed_html_tag(node) {
    // Regex won't work for tags with attrs, but we only
    // allow <del> anyway.
    const matches = /^<\/?(.*)>$/.exec(node.literal);
    if (matches && matches.length == 2) {
        const tag = matches[1];
        return ALLOWED_HTML_TAGS.indexOf(tag) > -1;
    }
    return false;
}

function html_if_tag_allowed(node) {
    if (is_allowed_html_tag(node)) {
        this.lit(node.literal);
        return;
    } else {
        this.lit(escape(node.literal));
    }
}

/*
 * Returns true if the parse output containing the node
 * comprises multiple block level elements (ie. lines),
 * or false if it is only a single line.
 */
function is_multi_line(node) {
    var par = node;
    while (par.parent) {
        par = par.parent;
    }
    return par.firstChild != par.lastChild;
}

/**
 * Class that wraps commonmark, adding the ability to see whether
 * a given message actually uses any markdown syntax or whether
 * it's plain text.
 */
export default class Markdown {
    constructor(input) {
        this.input = input;

        const parser = new commonmark.Parser();
        this.parsed = parser.parse(this.input);
    }

    isPlainText() {
        const walker = this.parsed.walker();

        let ev;
        while ( (ev = walker.next()) ) {
            const node = ev.node;
            if (TEXT_NODES.indexOf(node.type) > -1) {
                // definitely text
                continue;
            } else if (node.type == 'html_inline' || node.type == 'html_block') {
                // if it's an allowed html tag, we need to render it and therefore
                // we will need to use HTML. If it's not allowed, it's not HTML since
                // we'll just be treating it as text.
                if (is_allowed_html_tag(node)) {
                    return false;
                }
            } else {
                return false;
            }
        }
        return true;
    }

    toHTML() {
        const renderer = new commonmark.HtmlRenderer({
            safe: false,

            // Set soft breaks to hard HTML breaks: commonmark
            // puts softbreaks in for multiple lines in a blockquote,
            // so if these are just newline characters then the
            // block quote ends up all on one line
            // (https://github.com/vector-im/riot-web/issues/3154)
            softbreak: '<br />',
        });
        const real_paragraph = renderer.paragraph;

        renderer.paragraph = function(node, entering) {
            // If there is only one top level node, just return the
            // bare text: it's a single line of text and so should be
            // 'inline', rather than unnecessarily wrapped in its own
            // p tag. If, however, we have multiple nodes, each gets
            // its own p tag to keep them as separate paragraphs.
            if (is_multi_line(node)) {
                real_paragraph.call(this, node, entering);
            }
        };

        renderer.html_inline = html_if_tag_allowed;
        renderer.html_block = function(node) {
            // as with `paragraph`, we only insert line breaks
            // if there are multiple lines in the markdown.
            const isMultiLine = is_multi_line(node);

            if (isMultiLine) this.cr();
            html_if_tag_allowed.call(this, node);
            if (isMultiLine) this.cr();
        }

        return renderer.render(this.parsed);
    }

    /*
     * Render the markdown message to plain text. That is, essentially
     * just remove any backslashes escaping what would otherwise be
     * markdown syntax
     * (to fix https://github.com/vector-im/riot-web/issues/2870)
     */
    toPlaintext() {
        const renderer = new commonmark.HtmlRenderer({safe: false});
        const real_paragraph = renderer.paragraph;

        // The default `out` function only sends the input through an XML
        // escaping function, which causes messages to be entity encoded,
        // which we don't want in this case.
        renderer.out = function(s) {
            // The `lit` function adds a string literal to the output buffer.
            this.lit(s);
        };

        renderer.paragraph = function(node, entering) {
            // as with toHTML, only append lines to paragraphs if there are
            // multiple paragraphs
            if (is_multi_line(node)) {
                if (!entering && node.next) {
                    this.lit('\n\n');
                }
            }
        };
        renderer.html_block = function(node) {
            this.lit(node.literal);
            if (is_multi_line(node) && node.next) this.lit('\n\n');
        }

        return renderer.render(this.parsed);
    }
}
<|endoftext|>


<commit_before>//
//  REST_execute.js
//
//	a RESTful handler for the /start /stop /query request handling
//	NOTE: these are currently loaded in SR.Component.REST
//

var l_name = 'Monitor.execute';
var l_handles = exports.REST_handles = {};

const ansi_up = require('ansi_up');				// converts console message to colorful HTML
const humanize = require('humanize');			// better human readability

// handle server starting requests
l_handles.start = function (path_array, res, para, req) {
	var owner = path_array[2];
	var project = path_array[3];
	var name = path_array[4];

	if (owner === undefined || project === undefined) {
		return SR.REST.reply(res, 'cannot start server, missing owner/project');
	}

	var size = (path_array[5] === undefined ? 1 : parseInt(path_array[5]));

	var serverExist = false;
	for (let server of SR.startedServers) {
		if (server.owner == owner && server.project == project && server.name == name) {
			serverExist = true;
			break;
		}
	}
	!serverExist && SR.startedServers.push({
		owner: owner,
		project: project,
		name: name,
		size: size
	});

	SR.fs.writeFile("./startedServers.txt", JSON.stringify(SR.startedServers), function(err) {
		if(err) {
			return console.log(err);
		}
		LOG.warn("Started server logged.", l_name);
	});

	SR.API._START_SERVER({
		owner: owner,
		project: project,
		name: name,
		size: size,
		onOutput: function (output) {

			// make output HTML displable
			// TODO: do this at the client to save bandwidth?
			if (SR.Settings.REFORMAT_HTML_TEXT === true)
				output.data = humanize.nl2br(ansi_up.ansi_to_html(output.data));

			// record to file
			SR.StreamManager.publish(output.id, output);
		}
	}, function (err, list) {
		if (err) {
			LOG.error(err, l_name);
			return SR.REST.reply(res, []);
		}

		LOG.warn('execute success:', l_name);
		LOG.warn(list, l_name);
		SR.REST.reply(res, list);
	});
}

// handle server stopping requests
l_handles.stop = function (path_array, res, para, req) {

	var id = path_array[2];
	LOG.warn('id: [' + id + ']', 'stop');

	if (id === undefined) {
		return SR.REST.reply(res, 'need to specify serverID');
	} else if (id === 'self') {
		LOG.warn('received self shutdown request', 'SR.REST');
		SR.Settings.FRONTIER.dispose();
		return;
	}

	SR.Execute.stop(id, function (msg) {
		LOG.warn('SR.Execute.stop done', 'SR.REST');
		SR.REST.reply(res, msg);
	});
}

// handle server query requests
l_handles.query = function (path_array, res, para, req) {

	var owner   = path_array[2];
	var project = path_array[3];
	var name    = path_array[4];

	SR.Execute.query({owner: owner, project: project, name: name}, function (list) {
		LOG.sys('SR.Execute.query return info on ' + list.length + ' servers', 'SR.REST');
		SR.REST.reply(res, list);
	});
}

SR.REST.addHandler(l_handles);
<commit_msg>log started server with its id<commit_after>//
//  REST_execute.js
//
//	a RESTful handler for the /start /stop /query request handling
//	NOTE: these are currently loaded in SR.Component.REST
//

var l_name = 'Monitor.execute';
var l_handles = exports.REST_handles = {};

const ansi_up = require('ansi_up');				// converts console message to colorful HTML
const humanize = require('humanize');			// better human readability

// handle server starting requests
l_handles.start = function (path_array, res, para, req) {
	var owner = path_array[2];
	var project = path_array[3];
	var name = path_array[4];

	if (owner === undefined || project === undefined) {
		return SR.REST.reply(res, 'cannot start server, missing owner/project');
	}

	var size = (path_array[5] === undefined ? 1 : parseInt(path_array[5]));

	SR.API._START_SERVER({
		owner: owner,
		project: project,
		name: name,
		size: size,
		onOutput: function (output) {

			// make output HTML displable
			// TODO: do this at the client to save bandwidth?
			if (SR.Settings.REFORMAT_HTML_TEXT === true)
				output.data = humanize.nl2br(ansi_up.ansi_to_html(output.data));

			// record to file
			SR.StreamManager.publish(output.id, output);
		}
	}, function (err, list) {
		if (err) {
			LOG.error(err, l_name);
			return SR.REST.reply(res, []);
		}

		l_getServerInfo({owner, project, name, size});

		LOG.warn('execute success:', l_name);
		LOG.warn(list, l_name);
		SR.REST.reply(res, list);
	});
}

function l_getServerInfo(data) {
	LOG.warn('Trying to get info of the just started server.', l_name);
	SR.API._QUERY_SERVERS(data, function (err, result) {
		if (err) {
			LOG.error(err, l_name);
		} else {
			if (!result[0] || !result[0].id) {
				setTimeout(function () {
					l_getServerInfo(data);
				}, 500);
			} else {
				l_logStartedServers(Object.assign({}, data, {id: result[0].id}))
			}
		}
	})
}

function l_logStartedServers(serverData) {
	var serverExist = false;
	for (let server of SR.startedServers) {
		if (server.owner == serverData.owner && server.project == serverData.project && server.name == serverData.name) {
			serverExist = true;
			break;
		}
	}
	!serverExist && SR.startedServers.push({
		id: serverData.id,
		owner: serverData.owner,
		project: serverData.project,
		name: serverData.name,
		size: serverData.size
	});

	SR.fs.writeFile("./startedServers.txt", JSON.stringify(SR.startedServers), function(err) {
		if(err) {
			return console.log(err);
		}
		LOG.warn("Started server logged.", l_name);
	});
}

// handle server stopping requests
l_handles.stop = function (path_array, res, para, req) {

	var id = path_array[2];
	LOG.warn('id: [' + id + ']', 'stop');

	if (id === undefined) {
		return SR.REST.reply(res, 'need to specify serverID');
	} else if (id === 'self') {
		LOG.warn('received self shutdown request', 'SR.REST');
		SR.Settings.FRONTIER.dispose();
		return;
	}

	SR.Execute.stop(id, function (msg) {
		LOG.warn('SR.Execute.stop done', 'SR.REST');
		SR.REST.reply(res, msg);
	});
}

// handle server query requests
l_handles.query = function (path_array, res, para, req) {

	var owner   = path_array[2];
	var project = path_array[3];
	var name    = path_array[4];

	SR.Execute.query({owner: owner, project: project, name: name}, function (list) {
		LOG.sys('SR.Execute.query return info on ' + list.length + ' servers', 'SR.REST');
		SR.REST.reply(res, list);
	});
}

SR.REST.addHandler(l_handles);
<|endoftext|>