<commit_before>( function () {
	var scripts, uri,
		ua = navigator.userAgent;

	// Browsers with outdated or limited JavaScript engines get the no-JS experience
	if (
		// Internet Explorer < 8
		( ua.indexOf( 'MSIE' ) !== -1 && parseFloat( ua.split( 'MSIE' )[1] ) < 8 ) ||
		// Firefox < 3
		( ua.indexOf( 'Firefox/' ) !== -1 && parseFloat( ua.split( 'Firefox/' )[1] ) < 3 ) ||
		// Opera < 12
		( ua.indexOf( 'Opera/' ) !== -1 && ( ua.indexOf( 'Version/' ) === -1 ?
			// "Opera/x.y"
			parseFloat( ua.split( 'Opera/' )[1] ) < 10 :
			// "Opera/9.80 ... Version/x.y"
			parseFloat( ua.split( 'Version/' )[1] ) < 12
		) ) ||
		// "Mozilla/0.0 ... Opera x.y"
		( ua.indexOf( 'Opera ' ) !== -1 && parseFloat( ua.split( ' Opera ' )[1] ) < 10 ) ||
		// BlackBerry < 6
		ua.match( /BlackBerry[^\/]*\/[1-5]\./ ) ||
		// Open WebOS < 1.5
		ua.match( /webOS\/1\.[0-4]/ ) ||
		// Anything PlayStation based.
		ua.match( /PlayStation/i ) ||
		// Any Symbian based browsers
		ua.match( /SymbianOS|Series60/ ) ||
		// Any NetFront based browser
		ua.match( /NetFront/ ) ||
		// Opera Mini, all versions
		ua.match( /Opera Mini/ ) ||
		// Nokia's Ovi Browser
		ua.match( /S40OviBrowser/ ) ||
		// Google Glass browser groks JS but UI is too limited
		( ua.match( /Glass/ ) && ua.match( /Android/ ) )
	) {
		return;
	}

	// Detect palo base URI
	scripts = document.getElementsByTagName( 'script' );
	uri = scripts[scripts.length - 1].src.replace( /\\/g, '/' ).replace( /\/[^\/]*\/?$/, '' );

	// Add global palo object
	window.Palo = {
		packages: {},
		dependents: {},
		available: {},
		loading: {},
		resolving: {},
		done: {},
		uri: uri,

		/**
		 * Specify which packages are available on the server.
		 *
		 * A call to this function is appended to the end of this script by the server, and once it
		 * is called the method will remove itself.
		 *
		 * @param {Array[]} pkgs List of packages, each an array of name string and version
		 *   timestamp number elements followed by an optional dependencies element containing an
		 *   array of indicies referring to packages in the main array
		 * @param {[type]} epoch Version timestamp offset which package version timestamps will be
		 *   increased by, should be the minimum timestamp of all packages
		 * @param {string[]} initial List of package names to be initially loaded
		 */
		startup: function ( list, epoch, initial ) {
			var i, j, iLen, jLen, item, name, deps, time;

			// Unpack list of packages
			for ( i = 0, iLen = list.length; i < iLen; i++ ) {
				item = list[i];
				name = item[0];
				deps = item[2] || [];

				for ( j = 0, jLen = deps.length; j < jLen; j++ ) {
					deps[j] = list[deps[j]][0];
					if ( !this.dependents[deps[j]] ) {
						this.dependents[deps[j]] = [ name ];
					} else {
						this.dependents[deps[j]].push( name );
					}
				}

				this.packages[name] = {
					name: name,
					time: item[1] + epoch,
					deps: deps
				};
				// Mark this module as done
				( name === 'palo-startup' ? this.done : this.available )[name] = true;
			}

			// Calculate time of most recently changed initial package
			time = 0;
			i = initial.length;
			while ( i-- ) {
				time = Math.max( time, this.packages[initial[i]].time );
			}

			// Load initial packages
			/*jshint evil:true */
			document.write( '\u003Cscript src="' +
				this.uri + '/packages/' + initial.join( ';' ) + '?t=' + time +
			'"\u003E\u003C/script\u003E' );

			// Remove this method, preventing it from being run twice
			delete this.startup;
		},

		/**
		 * Placeholder for the proper implement method.
		 *
		 * Stores resources to be used by the loader, and executes the main module of the palo
		 * package, which implements the loader.
		 *
		 * @param {string} name Name of package
		 * @param {Object} res Package resources
		 */
		implement: function ( name, res ) {
			if ( !this.packages[name] ) {
				throw new Error( 'Implementation of unknown package: ' + name );
			}
			this.packages[name].res = res;
			if ( name === 'palo' ) {
				if ( !res.modules || typeof res.modules['.'] !== 'function' ) {
					throw new Error( 'Invalid module: palo' );
				}
				res.modules['.']();
			}
		}
	};
} )();
<commit_msg>Use local functions to improve minification<commit_after>( function () {
	var scripts, uri,
		ua = navigator.userAgent;

	function contains( str ) {
		return ua.indexOf( str ) !== -1;
	}

	function matches( pattern ) {
		return ua.match( pattern );
	}

	function version( str ) {
		return parseFloat( ua.split( str )[1] );
	}

	// Browsers with outdated or limited JavaScript engines get the no-JS experience
	if (
		// Internet Explorer < 8
		( contains( 'MSIE' ) && version( 'MSIE' ) < 8 ) ||
		// Firefox < 3
		( contains( 'Firefox/' ) && version( 'Firefox/' ) < 3 ) ||
		// Opera < 12
		( contains( 'Opera/' ) && (
			contains( 'Version/' ) ?
				// "Opera/9.80 ... Version/x.y"
				version( 'Version/' ) < 12 :
				// "Opera/x.y"
				version( 'Opera/' ) < 10
		) ) ||
		// "Mozilla/0.0 ... Opera x.y"
		( contains( 'Opera ' ) && version( ' Opera ' ) < 10 ) ||
		// BlackBerry < 6
		matches( /BlackBerry[^\/]*\/[1-5]\./ ) ||
		// Open WebOS < 1.5
		matches( /webOS\/1\.[0-4]/ ) ||
		// Anything PlayStation based.
		matches( /PlayStation/i ) ||
		// Any Symbian based browsers
		matches( /SymbianOS|Series60/ ) ||
		// Any NetFront based browser
		matches( /NetFront/ ) ||
		// Opera Mini, all versions
		matches( /Opera Mini/ ) ||
		// Nokia's Ovi Browser
		matches( /S40OviBrowser/ ) ||
		// Google Glass browser groks JS but UI is too limited
		( matches( /Glass/ ) && matches( /Android/ ) )
	) {
		return;
	}

	// Detect palo base URI
	scripts = document.getElementsByTagName( 'script' );
	uri = scripts[scripts.length - 1].src.replace( /\\/g, '/' ).replace( /\/[^\/]*\/?$/, '' );

	// Add global palo object
	window.Palo = {
		packages: {},
		dependents: {},
		available: {},
		loading: {},
		resolving: {},
		done: {},
		uri: uri,

		/**
		 * Specify which packages are available on the server.
		 *
		 * A call to this function is appended to the end of this script by the server, and once it
		 * is called the method will remove itself.
		 *
		 * @param {Array[]} pkgs List of packages, each an array of name string and version
		 *   timestamp number elements followed by an optional dependencies element containing an
		 *   array of indicies referring to packages in the main array
		 * @param {[type]} epoch Version timestamp offset which package version timestamps will be
		 *   increased by, should be the minimum timestamp of all packages
		 * @param {string[]} initial List of package names to be initially loaded
		 */
		startup: function ( list, epoch, initial ) {
			var i, j, iLen, jLen, item, name, deps, time;

			// Unpack list of packages
			for ( i = 0, iLen = list.length; i < iLen; i++ ) {
				item = list[i];
				name = item[0];
				deps = item[2] || [];

				for ( j = 0, jLen = deps.length; j < jLen; j++ ) {
					deps[j] = list[deps[j]][0];
					if ( !this.dependents[deps[j]] ) {
						this.dependents[deps[j]] = [ name ];
					} else {
						this.dependents[deps[j]].push( name );
					}
				}

				this.packages[name] = {
					name: name,
					time: item[1] + epoch,
					deps: deps
				};
				// Mark this module as done
				( name === 'palo-startup' ? this.done : this.available )[name] = true;
			}

			// Calculate time of most recently changed initial package
			time = 0;
			i = initial.length;
			while ( i-- ) {
				time = Math.max( time, this.packages[initial[i]].time );
			}

			// Load initial packages
			/*jshint evil:true */
			document.write( '\u003Cscript src="' +
				this.uri + '/packages/' + initial.join( ';' ) + '?t=' + time +
			'"\u003E\u003C/script\u003E' );

			// Remove this method, preventing it from being run twice
			delete this.startup;
		},

		/**
		 * Placeholder for the proper implement method.
		 *
		 * Stores resources to be used by the loader, and executes the main module of the palo
		 * package, which implements the loader.
		 *
		 * @param {string} name Name of package
		 * @param {Object} res Package resources
		 */
		implement: function ( name, res ) {
			if ( !this.packages[name] ) {
				throw new Error( 'Implementation of unknown package: ' + name );
			}
			this.packages[name].res = res;
			if ( name === 'palo' ) {
				if ( !res.modules || typeof res.modules['.'] !== 'function' ) {
					throw new Error( 'Invalid module: palo' );
				}
				res.modules['.']();
			}
		}
	};
} )();
<|endoftext|>


<commit_before>(function() {
  var
      doc = document,
      getElementById = 'getElementById',
      addEventListener = 'addEventListener',
      // http://www.theoreticallycorrect.com/Helmholtz-Pitch-Numbering/
      //
      // Lowest note in treble: 60
      // Highest note in treble: 83
      //
      // Subtract 32, then:
      // - % 24 gives MIDI note number minus 60
      // - / 24 gives duration: eighth, dotted quarter, quarter, half
      //
      // Other way:
      // note number - 28 + 24 * [0:eighth, 1:dotted quarter, 2:quarter, 3:half]
      //
      // http://i.ytimg.com/vi/bpBePVCUM7E/maxresdefault.jpg
      // https://www.youtube.com/watch?v=IBkH5_gLF8Q
      treble = [
        '`+,^,+Y),`.,C,^`\\Yq^.1e31H,01.,C,^`\\Yq',
        'T$$T,+)$),Y))<$TTYTl.).1^..D,\\.,<$TTTTT`'
      ],
      // All notes are eighths.
      // Subtract 64 to get MIDI note number - 33.
      bass = 'GNKNGNKNLSOSLSOSKSNSKSNSLSNSL@BCESOSESOSCOCOCOCOBNBN?J?J@CGL@@@@',
      bass2 = 'LSOSLSOSKSNSKSNSLSOSLSOSKSNSKSNS',
      // First 2 invisible lines, then 20 visible lines, then 2 for the Next display.
      grid = [],
      shadowGrid = [],
      w = 10,
      h = 22,
      s = w*h+20,
      // I J L O S T Z
      backgroundLUT = '#080808 #0dd #36f #e80 #dd0 #0e0 #c0c #f22 #002c2c #0a1433 #301b00 #2c2c00 #003000 #290029 #330707'.split(' '),
      // http://tetris.wikia.com/wiki/SRS
      //     1     2     4     8
      //    16    32    64   128
      //   256   512  1024  2048
      //  4096  8192 16384 32768
      shapes = [
        , // empty
        [240, 17476, 3840, 8738], // I
        [113, 550, 1136, 802], // J
        [116, 1570, 368, 547], // L
        [102, 102, 102, 102], // O
        [54, 1122, 864, 561], // S
        [114, 610, 624, 562], // T
        [99, 612, 1584, 306]], // Z
      leftRightRepeatDelta = .15,
      currentTetromino,
      currentX,
      currentY,
      currentRotation,
      state = 0, // 0=PLAYING, 1=LOST
      fillRows,
      score = 0,
      lines = 0,
      level = 1,
      gravityTimer, // between 0 and 1
      lockTimer = 0,
      bag = [],
      keysPressed = [],
      delta,
      lastFrame,
      i, j, x, y, tmp, tmp2, tmp3,
      // TODO instructions, credits
      html = '

'
      ;

  doc[addEventListener]('DOMContentLoaded', function() {
    treble[0] = treble[0] + treble[0] + 'xtvstqpsxtvs\\`}|x';
    treble[1] = treble[1] + treble[1] + 'tqspqqpptqspY`xx\u007f';
    bass = bass + bass + bass2 + bass2;

    tmp2 = '" style="width:20px;height:20px;float:left;box-shadow:-2px -2px 8px rgba(0,0,0,0.4) inset, 0 0 2px #000 inset;">
';
    for (i = 0; i < s; i++) {
      grid.push(0);
      if (i > 19 && i < 220) {
        html += '<div id="tis-' + i + tmp2;
      }
    }

    html += '

Next
';
    for (i = 220; i < s; i++) {
      if (i % w < 4) {
        html += '> 8) & 0xff, ((tmp2 + 36) >> 16), 0, // data size + 36 (little-endian)
      0x57, 0x41, 0x56, 0x45, // "WAVE"

      0x66, 0x6d, 0x74, 0x20, // "fmt "
      16, 0, 0, 0, // size of this subchunk
      1, 0, // PCM
      1, 0, // mono
      34, 86, 0, 0, // sample rate: 22050 Hz
      34, 86, 0, 0, // byte rate: 22050 bytes/s
      1, 0, // block align
      8, 0, // bits per sample

      0x64, 0x61, 0x74, 0x61, // "data"
      tmp2 & 0xFF, (tmp2 >> 8) & 0xff, tmp2 >> 16, 0 // data size
    ]);
    // delta is treble voice index
    for (delta = 0; delta < 2; delta++) {
      for (i = 44, j = 0; j < treble[delta].length; j++) {
        tmp3 = treble[delta].charCodeAt(j) - 32;
        x = 2 * Math.PI * 261.63 * Math.pow(2, (tmp3 % 24) / 12) / 22050;
        tmp2 = 0.2 - 0.1 * delta;
        for (y = 0; y < 4593 * [1, 3, 2, 4][Math.floor(tmp3 / 24)]; y++) {
          tmp[i++] += 127 * ((1-delta) + tmp2 * (Math.sin(y * x) > 0 ? 1 : -1));
          tmp2 *= 0.9999;
        }
      }
    }
    for (i = 44, j = 0; j < bass.length; j++) {
      tmp3 = bass.charCodeAt(j) - 64;
      x = 2 * Math.PI * 55 * Math.pow(2, tmp3 / 12) / 22050;
      tmp2 = 0.1;
      for (y = 0; y < 4593; y++) {
        tmp[i++] += 127 * tmp2 * (Math.sin(y * x) > 0 ? 1 : -1);
        tmp2 *= 0.9999;
      }
    }
    html += '<audio id="tis-music" src="' + URL.createObjectURL(new Blob([tmp], {type: 'audio/wav'})) + '" loop' + (location.hash == '#m' ? '' : ' autoplay') + '></audio>';

    tmp = doc.createElement('div');
    tmp.innerHTML = html;
    doc.body.appendChild(tmp);

    function isSolidAt(x, y, rotation, tetromino) {
      return currentTetromino &&
        (x&3) == x && (y&3) == y && // range check for [0, 4)
        (shapes[tetromino || currentTetromino][rotation] & (1 << (4 * y + x)));
    }

    function render() {
      for (tmp = currentY; currentTetromino && !isBlocked(currentX, tmp+1, currentRotation); tmp++);
      for (y = 0; y < h+4; y++) {
        for (x = 0; x < w; x++) {
          i = y*w + x;
          if (y >= h) {
            grid[i] = isSolidAt(x, y-h, 0, bag[0]) ? bag[0] : 0;
          }
          shadowGrid[i] =
            isSolidAt(x-currentX, y-currentY, currentRotation) ? currentTetromino :
            isSolidAt(x-currentX, y-tmp, currentRotation) ? currentTetromino + 7 :
            grid[i] || 0;
          if (tmp3 = doc[getElementById]('tis-' + i)) {
            tmp3.style.background = backgroundLUT[shadowGrid[i]];
          }
        }
      }
      tmp = '
';
      doc[getElementById]('tis-status').innerHTML = 'Score' + tmp + score + '
Lines' + tmp + lines + '
Level' + tmp + level + '
';
    }

    // XXX can probably shrink this by doing tryMove(dx, dy, dr) instead
    function isBlocked(posX, posY, rotation) {
      for (y = posY; y < posY+4; y++) {
        for (x = posX; x < posX+4; x++) {
          if (isSolidAt(x-posX, y-posY, rotation) &&
              (x < 0 || x >= w || y < 0 || y >= h || grid[y*w + x])) {
            return 1;
          }
        }
      }
      return 0;
    }

    function frame(now) {
      delta = (now - lastFrame) / 1e3 || 0;
      lastFrame = now;
      switch (state) {
        case 1:
          if (fillRows > 1) {
            for (x = 0; x < w; x++) {
              grid[fillRows*w + x] = 1 + Math.floor(Math.random() * 7);
            }
            render();
            fillRows--;
          }
          break;

        case 0:
          // Handle keyboard input
          for (tmp2 in keysPressed) {
            switch (parseInt(tmp2)) {
              case 37:
                // Left
                if (keysPressed[tmp2] < 0) break;
                keysPressed[tmp2] -= leftRightRepeatDelta;
                if (!isBlocked(currentX - 1, currentY, currentRotation)) {
                  currentX--;
                  lockTimer = 0;
                  render();
                }
                break;
              case 39:
                // Right
                if (keysPressed[tmp2] < 0) break;
                keysPressed[tmp2] -= leftRightRepeatDelta;
                if (!isBlocked(currentX + 1, currentY, currentRotation)) {
                  currentX++;
                  lockTimer = 0;
                  render();
                }
                break;
              case 38:
                // Up: hard drop
                if (keysPressed[tmp2]) break;
                while (!isBlocked(currentX, currentY + 1, currentRotation)) currentY++;
                lockTimer = 9;
                break;
              case 90: // z
              case 186: // ; (dvorak)
                // Rotate left
                // TODO wall kicks
                // http://web.archive.org/web/20081216145551/http://www.the-shell.net/img/srs_study.html
                if (!keysPressed[tmp2] && !isBlocked(currentX, currentY, (currentRotation+3) % 4)) {
                  currentRotation = (currentRotation+3)%4;
                  lockTimer = 0;
                  render();
                }
                break;
              case 88: // x
              case 81: // q (dvorak)
                // Rotate right
                if (!keysPressed[tmp2] && !isBlocked(currentX, currentY, (currentRotation+1) % 4)) {
                  currentRotation = (currentRotation+1)%4;
                  lockTimer = 0;
                  render();
                }
                break;
            }
            keysPressed[tmp2] += delta;
          }

          // Apply gravity
          gravityTimer += Math.max(
              keysPressed[40] ? 0.2 : 0,
              // TODO tune speed (use spreadsheet?)
              delta * (level+1) / 1.5);
          if (gravityTimer > 1) {
            gravityTimer = 0;
            if (!isBlocked(currentX, currentY + 1, currentRotation)) {
              currentY++;
              lockTimer = 0;
              render();
            }
          }

          if (!currentTetromino || lockTimer > 1 && isBlocked(currentX, currentY + 1, currentRotation)) {
            // Lock it in place
            render();
            for (i = 0; i < s; i++) grid[i] = shadowGrid[i];

            // Find full rows
            tmp2 = 0;
            for (y = 0; y < h; y++) {
              tmp = 1;
              for (x = 0; x < w; x++) {
                if (!grid[y*w + x]) {
                  tmp = 0;
                  break;
                }
              }
              if (tmp) {
                // Clear line
                // TODO animation
                tmp2++;
                for (i = y*w+w-1; i >= 0; i--) {
                  grid[i] = grid[i-w];
                }
              }
            }
            score += [0, 100, 300, 500, 800][tmp2] * level;
            lines += tmp2;
            level = 1 + Math.floor(lines / 10);

            // Shuffle bag if needed
            if (bag.length < 2) {
              tmp = 1;
              for (i = 0; i < 7; i++) {
                j = 0;
                while (tmp & (1 << j)) {
                  j = Math.ceil(Math.random() * 7);
                }
                tmp |= 1 << j;
                bag.push(j);
              }
            }

            // Spawn new tetromino
            currentTetromino = bag.shift();
            currentX = 3;
            currentY = 0;
            currentRotation = 0;
            gravityTimer = 0;
            lockTimer = 0;

            if (isBlocked(currentX, currentY, currentRotation)) {
              // Game over
              state = 1;
              fillRows = h;
            }
            render();
          }
          lockTimer += delta;
      }

      window.requestAnimationFrame(frame);
    }
    frame(0);

    function onKeyDown(e) {
      if (e.keyCode == 77) {
        tmp = doc[getElementById]('tis-music');
        if (tmp.paused) tmp.play(); else tmp.pause();
      }
      if (!keysPressed[e.keyCode]) {
        keysPressed[e.keyCode] = 0;
      }
      if (!(e.ctrlKey || e.shiftKey || e.altKey || e.metaKey)) {
        e.preventDefault();
      }
    }
    function onKeyUp(e) {
      delete keysPressed[e.keyCode];
    }
    doc[addEventListener]('keydown', onKeyDown);
    doc[addEventListener]('keyup', onKeyUp);
  });
})();
<commit_msg>Shrink music and synth<commit_after>(function() {
  var
      doc = document,
      getElementById = 'getElementById',
      addEventListener = 'addEventListener',
      music = [
        // http://www.theoreticallycorrect.com/Helmholtz-Pitch-Numbering/
        //
        // Lowest note in treble: 60
        // Highest note in treble: 83
        //
        // Subtract 32, then:
        // - % 24 gives MIDI note number minus 60
        // - / 24 gives duration: eighth, dotted quarter, quarter, half
        //
        // Other way:
        // note number - 28 + 24 * [0:eighth, 1:dotted quarter, 2:quarter, 3:half]
        //
        // http://i.ytimg.com/vi/bpBePVCUM7E/maxresdefault.jpg
        // https://www.youtube.com/watch?v=IBkH5_gLF8Q
        '`+,^,+Y),`.,C,^`\\Yq^.1e31H,01.,C,^`\\Yq',
        'T$$T,+)$),Y))<$TTYTl.).1^..D,\\.,<$TTTTT`',
        // All notes are eighths.
        // Subtract 64 to get MIDI note number - 33.
        'GNKNGNKNLSOSLSOSKSNSKSNSLSNSL@BCESOSESOSCOCOCOCOBNBN?J?J@CGL@@@@'
      ],
      thirdVerse = [
        'xtvstqpsxtvs\\`}|x',
        'tqspqqpptqspY`xx\u007f',
        'LSOSLSOSKSNSKSNSLSOSLSOSKSNSKSNS'
      ],
      // First 2 invisible lines, then 20 visible lines, then 2 for the Next display.
      grid = [],
      shadowGrid = [],
      w = 10,
      h = 22,
      s = w*h+20,
      // I J L O S T Z
      backgroundLUT = '#080808 #0dd #36f #e80 #dd0 #0e0 #c0c #f22 #002c2c #0a1433 #301b00 #2c2c00 #003000 #290029 #330707'.split(' '),
      // http://tetris.wikia.com/wiki/SRS
      //     1     2     4     8
      //    16    32    64   128
      //   256   512  1024  2048
      //  4096  8192 16384 32768
      shapes = [
        , // empty
        [240, 17476, 3840, 8738], // I
        [113, 550, 1136, 802], // J
        [116, 1570, 368, 547], // L
        [102, 102, 102, 102], // O
        [54, 1122, 864, 561], // S
        [114, 610, 624, 562], // T
        [99, 612, 1584, 306]], // Z
      leftRightRepeatDelta = .15,
      currentTetromino,
      currentX,
      currentY,
      currentRotation,
      state = 0, // 0=PLAYING, 1=LOST
      fillRows,
      score = 0,
      lines = 0,
      level = 1,
      gravityTimer, // between 0 and 1
      lockTimer = 0,
      bag = [],
      keysPressed = [],
      delta,
      lastFrame,
      i, j, x, y, tmp, tmp2, tmp3,
      // TODO instructions, credits
      html = '

'
      ;

  doc[addEventListener]('DOMContentLoaded', function() {
    thirdVerse[2] += thirdVerse[2];
    for (i in music) {
      music[i] += music[i] + thirdVerse[i];
    }

    tmp2 = '" style="width:20px;height:20px;float:left;box-shadow:-2px -2px 8px rgba(0,0,0,0.4) inset, 0 0 2px #000 inset;">
';
    for (i = 0; i < s; i++) {
      grid.push(0);
      if (i > 19 && i < 220) {
        html += '<div id="tis-' + i + tmp2;
      }
    }

    html += '

Next
';
    for (i = 220; i < s; i++) {
      if (i % w < 4) {
        html += '> 8) & 0xff, ((tmp2 + 36) >> 16), 0, // data size + 36 (little-endian)
      0x57, 0x41, 0x56, 0x45, // "WAVE"

      0x66, 0x6d, 0x74, 0x20, // "fmt "
      16, 0, 0, 0, // size of this subchunk
      1, 0, // PCM
      1, 0, // mono
      34, 86, 0, 0, // sample rate: 22050 Hz
      34, 86, 0, 0, // byte rate: 22050 bytes/s
      1, 0, // block align
      8, 0, // bits per sample

      0x64, 0x61, 0x74, 0x61, // "data"
      tmp2 & 0xFF, (tmp2 >> 8) & 0xff, tmp2 >> 16, 0 // data size
    ]);
    // delta is voice index
    for (delta = 0; delta < 3; delta++) {
      for (i = 44, j = 0; j < music[delta].length; j++) {
        tmp3 = music[delta].charCodeAt(j) - (delta == 2 ? 64 : 32);
        x = 2 * Math.PI * (delta == 2 ? 55 : 261.63) * Math.pow(2, (tmp3 % 24) / 12) / 22050;
        tmp2 = [.2, .1, .1][delta];
        for (y = 0; y < 4593 * [1, 3, 2, 4][Math.floor(tmp3 / 24)]; y++) {
          tmp[i++] += 127 * ((!delta) + tmp2 * (Math.sin(y * x) > 0 ? 1 : -1));
          tmp2 *= 0.9999;
        }
      }
    }
    html += '<audio id="tis-music" src="' + URL.createObjectURL(new Blob([tmp], {type: 'audio/wav'})) + '" loop' + (location.hash == '#m' ? '' : ' autoplay') + '></audio>';

    tmp = doc.createElement('div');
    tmp.innerHTML = html;
    doc.body.appendChild(tmp);

    function isSolidAt(x, y, rotation, tetromino) {
      return currentTetromino &&
        (x&3) == x && (y&3) == y && // range check for [0, 4)
        (shapes[tetromino || currentTetromino][rotation] & (1 << (4 * y + x)));
    }

    function render() {
      for (tmp = currentY; currentTetromino && !isBlocked(currentX, tmp+1, currentRotation); tmp++);
      for (y = 0; y < h+4; y++) {
        for (x = 0; x < w; x++) {
          i = y*w + x;
          if (y >= h) {
            grid[i] = isSolidAt(x, y-h, 0, bag[0]) ? bag[0] : 0;
          }
          shadowGrid[i] =
            isSolidAt(x-currentX, y-currentY, currentRotation) ? currentTetromino :
            isSolidAt(x-currentX, y-tmp, currentRotation) ? currentTetromino + 7 :
            grid[i] || 0;
          if (tmp3 = doc[getElementById]('tis-' + i)) {
            tmp3.style.background = backgroundLUT[shadowGrid[i]];
          }
        }
      }
      tmp = '
';
      doc[getElementById]('tis-status').innerHTML = 'Score' + tmp + score + '
Lines' + tmp + lines + '
Level' + tmp + level + '
';
    }

    // XXX can probably shrink this by doing tryMove(dx, dy, dr) instead
    function isBlocked(posX, posY, rotation) {
      for (y = posY; y < posY+4; y++) {
        for (x = posX; x < posX+4; x++) {
          if (isSolidAt(x-posX, y-posY, rotation) &&
              (x < 0 || x >= w || y < 0 || y >= h || grid[y*w + x])) {
            return 1;
          }
        }
      }
      return 0;
    }

    function frame(now) {
      delta = (now - lastFrame) / 1e3 || 0;
      lastFrame = now;
      switch (state) {
        case 1:
          if (fillRows > 1) {
            for (x = 0; x < w; x++) {
              grid[fillRows*w + x] = 1 + Math.floor(Math.random() * 7);
            }
            render();
            fillRows--;
          }
          break;

        case 0:
          // Handle keyboard input
          for (tmp2 in keysPressed) {
            switch (parseInt(tmp2)) {
              case 37:
                // Left
                if (keysPressed[tmp2] < 0) break;
                keysPressed[tmp2] -= leftRightRepeatDelta;
                if (!isBlocked(currentX - 1, currentY, currentRotation)) {
                  currentX--;
                  lockTimer = 0;
                  render();
                }
                break;
              case 39:
                // Right
                if (keysPressed[tmp2] < 0) break;
                keysPressed[tmp2] -= leftRightRepeatDelta;
                if (!isBlocked(currentX + 1, currentY, currentRotation)) {
                  currentX++;
                  lockTimer = 0;
                  render();
                }
                break;
              case 38:
                // Up: hard drop
                if (keysPressed[tmp2]) break;
                while (!isBlocked(currentX, currentY + 1, currentRotation)) currentY++;
                lockTimer = 9;
                break;
              case 90: // z
              case 186: // ; (dvorak)
                // Rotate left
                // TODO wall kicks
                // http://web.archive.org/web/20081216145551/http://www.the-shell.net/img/srs_study.html
                if (!keysPressed[tmp2] && !isBlocked(currentX, currentY, (currentRotation+3) % 4)) {
                  currentRotation = (currentRotation+3)%4;
                  lockTimer = 0;
                  render();
                }
                break;
              case 88: // x
              case 81: // q (dvorak)
                // Rotate right
                if (!keysPressed[tmp2] && !isBlocked(currentX, currentY, (currentRotation+1) % 4)) {
                  currentRotation = (currentRotation+1)%4;
                  lockTimer = 0;
                  render();
                }
                break;
            }
            keysPressed[tmp2] += delta;
          }

          // Apply gravity
          gravityTimer += Math.max(
              keysPressed[40] ? 0.2 : 0,
              // TODO tune speed (use spreadsheet?)
              delta * (level+1) / 1.5);
          if (gravityTimer > 1) {
            gravityTimer = 0;
            if (!isBlocked(currentX, currentY + 1, currentRotation)) {
              currentY++;
              lockTimer = 0;
              render();
            }
          }

          if (!currentTetromino || lockTimer > 1 && isBlocked(currentX, currentY + 1, currentRotation)) {
            // Lock it in place
            render();
            for (i = 0; i < s; i++) grid[i] = shadowGrid[i];

            // Find full rows
            tmp2 = 0;
            for (y = 0; y < h; y++) {
              tmp = 1;
              for (x = 0; x < w; x++) {
                if (!grid[y*w + x]) {
                  tmp = 0;
                  break;
                }
              }
              if (tmp) {
                // Clear line
                // TODO animation
                tmp2++;
                for (i = y*w+w-1; i >= 0; i--) {
                  grid[i] = grid[i-w];
                }
              }
            }
            score += [0, 100, 300, 500, 800][tmp2] * level;
            lines += tmp2;
            level = 1 + Math.floor(lines / 10);

            // Shuffle bag if needed
            if (bag.length < 2) {
              tmp = 1;
              for (i = 0; i < 7; i++) {
                j = 0;
                while (tmp & (1 << j)) {
                  j = Math.ceil(Math.random() * 7);
                }
                tmp |= 1 << j;
                bag.push(j);
              }
            }

            // Spawn new tetromino
            currentTetromino = bag.shift();
            currentX = 3;
            currentY = 0;
            currentRotation = 0;
            gravityTimer = 0;
            lockTimer = 0;

            if (isBlocked(currentX, currentY, currentRotation)) {
              // Game over
              state = 1;
              fillRows = h;
            }
            render();
          }
          lockTimer += delta;
      }

      window.requestAnimationFrame(frame);
    }
    frame(0);

    function onKeyDown(e) {
      if (e.keyCode == 77) {
        tmp = doc[getElementById]('tis-music');
        if (tmp.paused) tmp.play(); else tmp.pause();
      }
      if (!keysPressed[e.keyCode]) {
        keysPressed[e.keyCode] = 0;
      }
      if (!(e.ctrlKey || e.shiftKey || e.altKey || e.metaKey)) {
        e.preventDefault();
      }
    }
    function onKeyUp(e) {
      delete keysPressed[e.keyCode];
    }
    doc[addEventListener]('keydown', onKeyDown);
    doc[addEventListener]('keyup', onKeyUp);
  });
})();
<|endoftext|>


<commit_before>'use strict';

require('dotenv').config();

const test = require('ava');
const chai = require('chai');
const sinon = require('sinon');
const sinonChai = require('sinon-chai');
const httpMocks = require('node-mocks-http');
const underscore = require('underscore');
const Promise = require('bluebird');
const logger = require('heroku-logger');

const helpers = require('../../../../lib/helpers');
const analyticsHelper = require('../../../../lib/helpers/analytics');
const cacheHelper = require('../../../../lib/helpers/cache');
const contentful = require('../../../../lib/contentful');
const stubs = require('../../../helpers/stubs');
const broadcastFactory = require('../../../helpers/factories/broadcast');

// stubs
const broadcastId = stubs.getBroadcastId();
const cache = cacheHelper.broadcasts;
const sendErrorResponseStub = underscore.noop;
const mockBroadcast = broadcastFactory.getValidBroadcast();
const broadcastLookupStub = () => Promise.resolve(mockBroadcast);
const broadcastLookupFailStub = () => Promise.reject({ message: 'Epic fail' });
const broadcastLookupNotFoundStub = () => Promise.reject({ status: 404 });

// setup "x.should.y" assertion style
chai.should();
chai.use(sinonChai);

// module to be tested
const getBroadcast = require('../../../../lib/middleware/import-message/broadcast-get');

// sinon sandbox object
const sandbox = sinon.sandbox.create();

test.beforeEach((t) => {
  stubs.stubLogger(sandbox, logger);
  sandbox.stub(analyticsHelper, 'addParameters')
    .returns(underscore.noop);
  sandbox.stub(helpers, 'sendErrorResponse')
    .returns(sendErrorResponseStub);
  // setup req, res mocks
  t.context.req = httpMocks.createRequest();
  t.context.req.broadcastId = broadcastId;
  t.context.res = httpMocks.createResponse();
});

test.afterEach((t) => {
  // reset stubs, spies, and mocks
  sandbox.restore();
  t.context = {};
});

/**
 * Tests
 */
test('getBroadcast should set broadcast from cache if cached', async (t) => {
  // setup
  const next = sinon.stub();
  const middleware = getBroadcast();
  sandbox.stub(cache, 'get')
    .returns(Promise.resolve(mockBroadcast));
  sandbox.stub(contentful, 'fetchBroadcast')
    .callsFake(broadcastLookupStub);
  sandbox.stub(cache, 'set')
    .returns(Promise.resolve(mockBroadcast));

  // test
  await middleware(t.context.req, t.context.res, next);
  cache.get.should.have.been.called;
  analyticsHelper.addParameters.should.have.been.called;
  contentful.fetchBroadcast.should.not.have.been.called;
  cache.set.should.not.have.been.called;
  t.context.req.broadcast.should.deep.equal(mockBroadcast);
  next.should.have.been.called;
});

test('getBroadcast should fetch from Contentful to set broadcast if not cached', async (t) => {
  // setup
  const next = sinon.stub();
  const middleware = getBroadcast();
  sandbox.stub(cache, 'get')
    .returns(Promise.resolve(null));
  sandbox.stub(contentful, 'fetchBroadcast')
    .callsFake(broadcastLookupStub);
  sandbox.stub(cache, 'set')
    .returns(Promise.resolve(mockBroadcast));

  // test
  await middleware(t.context.req, t.context.res, next);
  cache.get.should.have.been.called;
  analyticsHelper.addParameters.should.have.been.called;
  contentful.fetchBroadcast.should.have.been.called;
  cache.set.should.have.been.called;
  t.context.req.broadcast.should.deep.equal(mockBroadcast);
  next.should.have.been.called;
});

test('getBroadcast should call sendErrorResponse if broadcastId not found', async (t) => {
  // setup
  const next = sinon.stub();
  const middleware = getBroadcast();
  sandbox.stub(contentful, 'fetchBroadcast')
    .callsFake(broadcastLookupNotFoundStub);

  // test
  await middleware(t.context.req, t.context.res, next);
  analyticsHelper.addParameters.should.have.been.called;
  helpers.sendErrorResponse.should.have.been.called;
  t.context.req.should.not.have.property('broadcast');
  next.should.not.have.been.called;
});

test('getBroadcast should call sendErrorResponse if cache.get fails', async (t) => {
  // setup
  const next = sinon.stub();
  const middleware = getBroadcast();
  sandbox.stub(cache, 'get')
    .returns(Promise.reject(new Error()));

  // test
  await middleware(t.context.req, t.context.res, next);
  analyticsHelper.addParameters.should.have.been.called;
  helpers.sendErrorResponse.should.have.been.called;
  t.context.req.should.not.have.property('broadcast');
  next.should.not.have.been.called;
});

test('getBroadcast should call sendErrorResponse if contentful.fetchBroadcast fails', async (t) => {
  // setup
  const next = sinon.stub();
  const middleware = getBroadcast();
  t.context.req.broadcastId = 'fail';
  sandbox.stub(contentful, 'fetchBroadcast')
    .callsFake(broadcastLookupFailStub);

  // test
  await middleware(t.context.req, t.context.res, next);
  analyticsHelper.addParameters.should.have.been.called;
  helpers.sendErrorResponse.should.have.been.called;
  t.context.req.should.not.have.property('broadcast');
  next.should.not.have.been.called;
});
<commit_msg>Adds test for cache.set fail<commit_after>'use strict';

require('dotenv').config();

const test = require('ava');
const chai = require('chai');
const sinon = require('sinon');
const sinonChai = require('sinon-chai');
const httpMocks = require('node-mocks-http');
const underscore = require('underscore');
const Promise = require('bluebird');
const logger = require('heroku-logger');

const helpers = require('../../../../lib/helpers');
const analyticsHelper = require('../../../../lib/helpers/analytics');
const cacheHelper = require('../../../../lib/helpers/cache');
const contentful = require('../../../../lib/contentful');
const stubs = require('../../../helpers/stubs');
const broadcastFactory = require('../../../helpers/factories/broadcast');

// stubs
const broadcastId = stubs.getBroadcastId();
const cache = cacheHelper.broadcasts;
const sendErrorResponseStub = underscore.noop;
const mockBroadcast = broadcastFactory.getValidBroadcast();
const broadcastLookupStub = () => Promise.resolve(mockBroadcast);
const broadcastLookupFailStub = () => Promise.reject({ message: 'Epic fail' });
const broadcastLookupNotFoundStub = () => Promise.reject({ status: 404 });

// setup "x.should.y" assertion style
chai.should();
chai.use(sinonChai);

// module to be tested
const getBroadcast = require('../../../../lib/middleware/import-message/broadcast-get');

// sinon sandbox object
const sandbox = sinon.sandbox.create();

test.beforeEach((t) => {
  stubs.stubLogger(sandbox, logger);
  sandbox.stub(analyticsHelper, 'addParameters')
    .returns(underscore.noop);
  sandbox.stub(helpers, 'sendErrorResponse')
    .returns(sendErrorResponseStub);
  // setup req, res mocks
  t.context.req = httpMocks.createRequest();
  t.context.req.broadcastId = broadcastId;
  t.context.res = httpMocks.createResponse();
});

test.afterEach((t) => {
  // reset stubs, spies, and mocks
  sandbox.restore();
  t.context = {};
});

/**
 * Tests
 */
test('getBroadcast should set broadcast from cache if cached', async (t) => {
  // setup
  const next = sinon.stub();
  const middleware = getBroadcast();
  sandbox.stub(cache, 'get')
    .returns(Promise.resolve(mockBroadcast));
  sandbox.stub(contentful, 'fetchBroadcast')
    .callsFake(broadcastLookupStub);
  sandbox.stub(cache, 'set')
    .returns(Promise.resolve(mockBroadcast));

  // test
  await middleware(t.context.req, t.context.res, next);
  cache.get.should.have.been.called;
  analyticsHelper.addParameters.should.have.been.called;
  contentful.fetchBroadcast.should.not.have.been.called;
  cache.set.should.not.have.been.called;
  t.context.req.broadcast.should.deep.equal(mockBroadcast);
  next.should.have.been.called;
});

test('getBroadcast should fetch from Contentful to set broadcast if not cached', async (t) => {
  // setup
  const next = sinon.stub();
  const middleware = getBroadcast();
  sandbox.stub(cache, 'get')
    .returns(Promise.resolve(null));
  sandbox.stub(contentful, 'fetchBroadcast')
    .callsFake(broadcastLookupStub);
  sandbox.stub(cache, 'set')
    .returns(Promise.resolve(mockBroadcast));

  // test
  await middleware(t.context.req, t.context.res, next);
  cache.get.should.have.been.called;
  analyticsHelper.addParameters.should.have.been.called;
  contentful.fetchBroadcast.should.have.been.called;
  cache.set.should.have.been.called;
  t.context.req.broadcast.should.deep.equal(mockBroadcast);
  next.should.have.been.called;
});

test('getBroadcast should call sendErrorResponse if broadcastId not found', async (t) => {
  // setup
  const next = sinon.stub();
  const middleware = getBroadcast();
  sandbox.stub(contentful, 'fetchBroadcast')
    .callsFake(broadcastLookupNotFoundStub);

  // test
  await middleware(t.context.req, t.context.res, next);
  analyticsHelper.addParameters.should.have.been.called;
  helpers.sendErrorResponse.should.have.been.called;
  t.context.req.should.not.have.property('broadcast');
  next.should.not.have.been.called;
});

test('getBroadcast should call sendErrorResponse if cache.get fails', async (t) => {
  // setup
  const next = sinon.stub();
  const middleware = getBroadcast();
  sandbox.stub(cache, 'get')
    .returns(Promise.reject(new Error()));

  // test
  await middleware(t.context.req, t.context.res, next);
  analyticsHelper.addParameters.should.have.been.called;
  helpers.sendErrorResponse.should.have.been.called;
  t.context.req.should.not.have.property('broadcast');
  next.should.not.have.been.called;
});

test('getBroadcast should call sendErrorResponse if contentful.fetchBroadcast fails', async (t) => {
  // setup
  const next = sinon.stub();
  const middleware = getBroadcast();
  t.context.req.broadcastId = 'fail';
  sandbox.stub(contentful, 'fetchBroadcast')
    .callsFake(broadcastLookupFailStub);

  // test
  await middleware(t.context.req, t.context.res, next);
  analyticsHelper.addParameters.should.have.been.called;
  helpers.sendErrorResponse.should.have.been.called;
  t.context.req.should.not.have.property('broadcast');
  next.should.not.have.been.called;
});

test('getBroadcast should call sendErrorResponse if cache.set fails', async (t) => {
  // setup
  const next = sinon.stub();
  const middleware = getBroadcast();
  sandbox.stub(cache, 'get')
    .returns(Promise.resolve(null));
  sandbox.stub(contentful, 'fetchBroadcast')
    .callsFake(broadcastLookupStub);
  sandbox.stub(cache, 'set')
    .returns(Promise.reject(new Error()));

  // test
  await middleware(t.context.req, t.context.res, next);
  analyticsHelper.addParameters.should.have.been.called;
  cache.get.should.have.been.called;
  contentful.fetchBroadcast.should.have.been.called;
  helpers.sendErrorResponse.should.have.been.called;

  t.context.req.should.not.have.property('broadcast');
  next.should.not.have.been.called;
});
<|endoftext|>


<commit_before>"use strict";

var pgMap          = require("./pgMap.js");
var util           = require("../util.js");
var debug          = require("debug")("OSMBC:model:user");
var should         = require("should");
var async          = require("async");
var messageCenter  = require("../notification/messageCenter.js");
var mailReceiver   = require("../notification/mailReceiver.js");
var random         = require("randomstring");
var emailValidator = require("email-validator");
var config         = require("../config.js");
var cheerio        = require("cheerio");
var request        = require("request");
var path           = require("path");
var fs             = require("fs");

// generate an user object, use Prototpye
// to prototype some fields
function User (proto) {
  debug("User");
  debug("Prototype %s", JSON.stringify(proto));
  this.id = 0;
  for (var k in proto) {
    this[k] = proto[k];
  }
}

// return a new User Object (in memory)
// Optional: Protoype
function create (proto) {
  debug("create");
  return new User(proto);
}

// create a new user in the database
// avoid doublettes in OSMUser (osm account).
function createNewUser (proto, callback) {
  debug("createNewUser");
  if (typeof (proto) === "function") {
    callback = proto;
    proto = null;
  }
  if (proto) should.not.exist(proto.id);
  var user = create(proto);
  find({OSMUser: user.OSMUser}, function (err, result) {
    if (err) return callback(err);
    if (result && result.length > 0) {
      return callback(new Error("User >" + user.OSMUser + "< already exists."));
    }
    // set some defaults for the user
    if (!proto) user.mailNewCollection = "false";
    if (!proto) user.mailAllComment = "false";
    if (!proto) user.mailComment = [];
    if (!proto) user.mailBlogLanguageStatusChange = [];
    // save data
    user.save(function updateUser(err, result) {
      if (err) return callback(err, result);
      mailReceiver.updateUser(result);
      callback(null, result);
    });
  });
}

let avatarCache = path.join(__dirname, "..", "public", "ch_av");

function cacheOSMAvatar(osmuser, callback) {
  debug("cacheOSMAvatar %s", osmuser);
  var requestString = "https://www.openstreetmap.org/user/" + encodeURI(osmuser);
  request(requestString, function(err, response, body) {
    if (err) return callback(err, null);
    if (body) {
      let c = cheerio.load(body);
      let avatarLink = c(".user_image").attr("src");
      if (avatarLink === undefined) return callback();
      if (avatarLink.substring(0, 1) === "/") avatarLink = "https://www.openstreetmap.org" + avatarLink;
      request.get({url: avatarLink, encoding: "binary"}, function (err, response, body) {
        if (err) return callback(err);
        fs.writeFile(path.join(avatarCache, util.linkify(osmuser) + ".png"), body, "binary", function(err) {
          return callback(err);
        });
      });
    } else return callback();
  });
}

function cacheOSMAvatarAll(callback) {
  debug("cacheOSMAvatarAll");
  find({}, function(err, users) {
    if (err) return callback(err);
    async.eachLimit(users, 4, function (item, cb) {
      cacheOSMAvatar(item.OSMUser, cb);
    }, function(err) { return callback(err); });
  });
}
cacheOSMAvatarAll(function(err) { if (err) console.err("Error during Cache of User Avatar" + err.message); });

// Calculate derived values
// now: Calculate only number of changes
User.prototype.calculateChanges = function calculateChanges(callback) {
  debug("User.prototype.calculateChanges");
  var self = this;
  if (self._countChanges) return;
  pgMap.count("select count(*) as count from changes where data->>'user'='" + this.OSMUser + "' and data->>'table'='article'", function(err, result) {
    if (err) return callback(err);
    self._countChanges = result.count;
    return callback();
  });
};

let htmlroot = config.getValue("htmlroot");

function getAvatar(osmuser) {
  debug("getAvatar");
  /* jshint -W040 */
  if (osmuser === undefined && this !== undefined) osmuser = this.OSMUser;
  /* jshint +W040 */
  return htmlroot + "/ch_av/" + util.linkify(osmuser) + ".png";
}

User.prototype.getAvatar = getAvatar;
module.exports.getAvatar = getAvatar;

// use some database function from pgMap
User.prototype.remove = pgMap.remove;

function find(obj, ord, callback) {
  debug("find");
  pgMap.find({table: "usert", create: create}, obj, ord, callback);
}
function findById(id, callback) {
  debug("findById %s", id);
  pgMap.findById(id, {table: "usert", create: create}, callback);
}

function findOne(obj1, obj2, callback) {
  debug("findOne");
  pgMap.findOne({table: "usert", create: create}, obj1, obj2, callback);
}

var pgObject = {};
pgObject.createString = "CREATE TABLE usert (  id bigserial NOT NULL,  data json,  \
                  CONSTRAINT user_pkey PRIMARY KEY (id) ) WITH (  OIDS=FALSE);";
pgObject.indexDefinition = {
  "user_id_idx": "CREATE INDEX user_id_idx ON usert USING btree (((data ->> 'OSMUser'::text)))"

};
pgObject.viewDefinition = {};
pgObject.table = "usert";
module.exports.pg = pgObject;

// This function is called by the link
// send out via EMail if someone registers a new email
User.prototype.validateEmail = function validateEmail(user, validationCode, callback) {
  debug("validateEmail");
  should(typeof (user)).eql("object");
  should(typeof (validationCode)).eql("string");
  should(typeof (callback)).eql("function");
  var self = this;
  var err;
  if (self.OSMUser !== user.OSMUser) {
    debug("User is wrong");
    err = new Error("Wrong User: expected >" + self.OSMUser + "< given >" + user.OSMUser + "<");
    return callback(err);
  }
  if (!self.emailInvalidation) {
    debug("nothing in validation");
    err = new Error("No Validation pending for user >" + self.OSMUser + "<");
    return callback(err);
  }
  if (validationCode !== self.emailValidationKey) {
    debug("Validation Code is wrong");
    err = new Error("Wrong Validation Code for EMail for user >" + self.OSMUser + "<");
    messageCenter.global.sendInfo({oid: self.id, user: user.OSMUser, table: "usert", property: "email", from: null, to: "Validation Failed"}, function() {
      return callback(err);
    });
    return;
  }
  debug("Email Validation OK saving User");
  var oldmail = self.email;
  self.email = self.emailInvalidation;
  delete self.emailInvalidation;
  delete self.emailValidationKey;
  self.save(function logit(err) {
    mailReceiver.updateUser(self);
    if (err) return callback(err);
    messageCenter.global.sendInfo({oid: self.id, user: user.OSMUser, table: "usert", property: "email", from: oldmail, to: self.email}, function() {
      return callback(err);
    });
  });
};

// pgMap setAndSave Function,
// Check on EMail change (and trigger new validation)
// create error if user that already have logged in
// changes their OSM Account
User.prototype.setAndSave = function setAndSave(user, data, callback) {
  debug("setAndSave");
  // reset cache
  util.requireTypes([user, data, callback], ["object", "object", "function"]);
  var self = this;
  delete self.lock;
  var sendWelcomeEmail = false;
  // remove spaces from front and and of email adress
  if (data.email) data.email = data.email.trim();

  // check and react on Mail Change
  if (data.email && data.email.trim() !== "" && data.email !== self.email) {
    if (self.OSMUser !== user.OSMUser && self.hasLoggedIn()) return callback(new Error("EMail address can only be changed by the user himself, after he has logged in."));
    if (data.email !== "resend") {
      if (!emailValidator.validate(data.email)) {
        var error = new Error("Invalid Email Address: " + data.email);
        return callback(error);
      }
      if (data.email !== "") {
        // put email to validation email, and generate a key.
        data.emailInvalidation = data.email;
        data.emailValidationKey = random.generate();
        delete data.email;
      }
    } else {
      // resend case.
      delete data.email;
    }
    sendWelcomeEmail = true;
  }
  // Check Change of OSMUser Name.
  if (data.OSMUser !== self.OSMUser) {
    if (self.hasLoggedIn()) return callback(new Error(">" + self.OSMUser + "< already has logged in, change in name not possible."));
  }
  async.series([
    function checkUserName(cb) {
      if (data.OSMUser && data.OSMUser !== self.OSMUser) {
        find({OSMUser: data.OSMUser}, function(err, result) {
          if (err) return callback(err);
          if (result && result.length) {
            return cb(new Error("User >" + data.OSMUser + "< already exists."));
          } else return cb();
        });
      } else return cb();
    },
    cacheOSMAvatar.bind(null, data.OSMUser)
  ], function finalFunction(err) {
    if (err) return callback(err);
    async.forEachOf(data, function setAndSaveEachOf(value, key, cbEachOf) {
      // There is no Value for the key, so do nothing
      if (typeof (value) === "undefined") return cbEachOf();

      // The Value to be set, is the same then in the object itself
      // so do nothing
      if (value === self[key]) return cbEachOf();
      if (JSON.stringify(value) === JSON.stringify(self[key])) return cbEachOf();
      if (typeof (self[key]) === "undefined" && value === "") return cbEachOf();

      debug("Set Key %s to value >>%s<<", key, value);
      debug("Old Value Was >>%s<<", self[key]);

      async.series([
        function(cb) {
          // do not log validation key in logfile
          var toValue = value;
          // Hide Validation Key not to show to all users
          if (key === "emailValidationKey") return cb();

          messageCenter.global.sendInfo({oid: self.id, user: user.OSMUser, table: "usert", property: key, from: self[key], to: toValue}, cb);
        },
        function(cb) {
          self[key] = value;
          cb();
        }
      ], function(err) {
        cbEachOf(err);
      });
    }, function setAndSaveFinalCB(err) {
      debug("setAndSaveFinalCB");
      if (err) return callback(err);
      self.save(function (err) {
        // Inform Mail Receiver Module, that there could be a change
        if (err) return callback(err);
        // tell mail receiver to update the information about the users
        mailReceiver.updateUser(self);
        if (sendWelcomeEmail) {
          var m = new mailReceiver.MailReceiver(self);
          // do not wait for mail to go out.
          // mail is logged in outgoing mail list
          m.sendWelcomeMail(user.OSMUser, function () {});
        }
        return callback();
      });
    });
  });
};

User.prototype.hasLoggedIn = function hasLoggedIn() {
  debug("User.prototype.hasLoggedIn");
  if (this.lastAccess) return true;
  return false;
};

User.prototype.getNotificationStatus = function getNotificationStatus(channel, type) {
  debug("User.prototype.getNotificationStatus");
  if (!this.notificationStatus) return null;
  if (!this.notificationStatus[channel]) return null;
  return this.notification[channel][type];
};

User.prototype.getMainLang = function getMainLang() {
  debug("User.prototype.getMainLang");
  if (this.mainLang) return this.mainLang;
  if (this.language) return this.language;
  return "EN";
};

User.prototype.getSecondLang = function getSecondLang() {
  debug("User.prototype.getMainLang");
  if (this.secondLang) return this.secondLang;
  return null;
};

User.prototype.getLang3 = function getLang3() {
  debug("User.prototype.getLang3");
  if (this.lang3) return this.lang3;
  return null;
};

User.prototype.getLang4 = function getLang4() {
  debug("User.prototype.getLang4");
  if (this.lang4) return this.lang4;
  return null;
};

User.prototype.setOption = function setOption(view, option, value) {
  debug("User.prototype.setOption");
  if (!this.option) this.option = {};
  if (!this.option[view]) this.option[view] = {};
  this.option[view][option] = value;
};

var defaultOption = {};

User.prototype.getOption = function getOption(view, option) {
  debug("User.protoype.getOption");
  if (this.option && this.option[view] && this.option[view][option]) return this.option[view][option];
  if (defaultOption && defaultOption[view] && defaultOption[view][option]) return defaultOption[view][option];
  return null;
};

User.prototype.createApiKey = function createApiKey(callback) {
  debug("createApiKey");
  let apiKey = random.generate({length: 10});
  this.apiKey = apiKey;
  this.save(callback);
};

// this functions returns (and caches) the new users
// Users that have done their first edit in the last month

var _newUsers = null;
let interval = config.getValue("WelcomeInterval", {mustExist: true});

module.exports.getNewUsers = function getNewUsers(callback) {
  debug("getNewUsers");
  if (_newUsers) return callback(null, _newUsers);

  pgMap.select("select data->>'user' as osmuser ,min(data->>'timestamp') as first from changes group by data->>'user' having ( min(data->>'timestamp')  )::timestamp with time zone  > current_timestamp - interval '" + interval + "'", function(err, result) {
    if (err) return callback(err);
    _newUsers = result;
    setTimeout(function() {
      _newUsers = null;
    }, 10 * 60 * 1000);
    callback(null, result);
  });
};

// Creates an User object and stores it to database
// can use a prototype to initialise data
// Parameter: Prototype (optional)
//            callback
// Prototype is not allowed to have an id
module.exports.createNewUser = createNewUser;

// save stores the current object to database
User.prototype.save = pgMap.save; // Create Tables and Views

module.exports.create = create;
module.exports.find = find;
module.exports.findById = findById;
module.exports.findOne = findOne;

User.prototype.getTable = function getTable() {
  return "usert";
};
<commit_msg>Disable user avatar cache initialisation in test mode.<commit_after>"use strict";

var pgMap          = require("./pgMap.js");
var util           = require("../util.js");
var debug          = require("debug")("OSMBC:model:user");
var should         = require("should");
var async          = require("async");
var messageCenter  = require("../notification/messageCenter.js");
var mailReceiver   = require("../notification/mailReceiver.js");
var random         = require("randomstring");
var emailValidator = require("email-validator");
var config         = require("../config.js");
var cheerio        = require("cheerio");
var request        = require("request");
var path           = require("path");
var fs             = require("fs");

// generate an user object, use Prototpye
// to prototype some fields
function User (proto) {
  debug("User");
  debug("Prototype %s", JSON.stringify(proto));
  this.id = 0;
  for (var k in proto) {
    this[k] = proto[k];
  }
}

// return a new User Object (in memory)
// Optional: Protoype
function create (proto) {
  debug("create");
  return new User(proto);
}

// create a new user in the database
// avoid doublettes in OSMUser (osm account).
function createNewUser (proto, callback) {
  debug("createNewUser");
  if (typeof (proto) === "function") {
    callback = proto;
    proto = null;
  }
  if (proto) should.not.exist(proto.id);
  var user = create(proto);
  find({OSMUser: user.OSMUser}, function (err, result) {
    if (err) return callback(err);
    if (result && result.length > 0) {
      return callback(new Error("User >" + user.OSMUser + "< already exists."));
    }
    // set some defaults for the user
    if (!proto) user.mailNewCollection = "false";
    if (!proto) user.mailAllComment = "false";
    if (!proto) user.mailComment = [];
    if (!proto) user.mailBlogLanguageStatusChange = [];
    // save data
    user.save(function updateUser(err, result) {
      if (err) return callback(err, result);
      mailReceiver.updateUser(result);
      callback(null, result);
    });
  });
}

let avatarCache = path.join(__dirname, "..", "public", "ch_av");

function cacheOSMAvatar(osmuser, callback) {
  debug("cacheOSMAvatar %s", osmuser);
  var requestString = "https://www.openstreetmap.org/user/" + encodeURI(osmuser);
  request(requestString, function(err, response, body) {
    if (err) return callback(err, null);
    if (body) {
      let c = cheerio.load(body);
      let avatarLink = c(".user_image").attr("src");
      if (avatarLink === undefined) return callback();
      if (avatarLink.substring(0, 1) === "/") avatarLink = "https://www.openstreetmap.org" + avatarLink;
      request.get({url: avatarLink, encoding: "binary"}, function (err, response, body) {
        if (err) return callback(err);
        fs.writeFile(path.join(avatarCache, util.linkify(osmuser) + ".png"), body, "binary", function(err) {
          return callback(err);
        });
      });
    } else return callback();
  });
}

function cacheOSMAvatarAll(callback) {
  debug("cacheOSMAvatarAll");
  find({}, function(err, users) {
    if (err) return callback(err);
    async.eachLimit(users, 4, function (item, cb) {
      cacheOSMAvatar(item.OSMUser, cb);
    }, function(err) { return callback(err); });
  });
}
if (process.env.node_env !== "test") {
  cacheOSMAvatarAll(function(err) { if (err) console.err("Error during Cache of User Avatar" + err.message); });
}

// Calculate derived values
// now: Calculate only number of changes
User.prototype.calculateChanges = function calculateChanges(callback) {
  debug("User.prototype.calculateChanges");
  var self = this;
  if (self._countChanges) return;
  pgMap.count("select count(*) as count from changes where data->>'user'='" + this.OSMUser + "' and data->>'table'='article'", function(err, result) {
    if (err) return callback(err);
    self._countChanges = result.count;
    return callback();
  });
};

let htmlroot = config.getValue("htmlroot");

function getAvatar(osmuser) {
  debug("getAvatar");
  /* jshint -W040 */
  if (osmuser === undefined && this !== undefined) osmuser = this.OSMUser;
  /* jshint +W040 */
  return htmlroot + "/ch_av/" + util.linkify(osmuser) + ".png";
}

User.prototype.getAvatar = getAvatar;
module.exports.getAvatar = getAvatar;

// use some database function from pgMap
User.prototype.remove = pgMap.remove;

function find(obj, ord, callback) {
  debug("find");
  pgMap.find({table: "usert", create: create}, obj, ord, callback);
}
function findById(id, callback) {
  debug("findById %s", id);
  pgMap.findById(id, {table: "usert", create: create}, callback);
}

function findOne(obj1, obj2, callback) {
  debug("findOne");
  pgMap.findOne({table: "usert", create: create}, obj1, obj2, callback);
}

var pgObject = {};
pgObject.createString = "CREATE TABLE usert (  id bigserial NOT NULL,  data json,  \
                  CONSTRAINT user_pkey PRIMARY KEY (id) ) WITH (  OIDS=FALSE);";
pgObject.indexDefinition = {
  "user_id_idx": "CREATE INDEX user_id_idx ON usert USING btree (((data ->> 'OSMUser'::text)))"

};
pgObject.viewDefinition = {};
pgObject.table = "usert";
module.exports.pg = pgObject;

// This function is called by the link
// send out via EMail if someone registers a new email
User.prototype.validateEmail = function validateEmail(user, validationCode, callback) {
  debug("validateEmail");
  should(typeof (user)).eql("object");
  should(typeof (validationCode)).eql("string");
  should(typeof (callback)).eql("function");
  var self = this;
  var err;
  if (self.OSMUser !== user.OSMUser) {
    debug("User is wrong");
    err = new Error("Wrong User: expected >" + self.OSMUser + "< given >" + user.OSMUser + "<");
    return callback(err);
  }
  if (!self.emailInvalidation) {
    debug("nothing in validation");
    err = new Error("No Validation pending for user >" + self.OSMUser + "<");
    return callback(err);
  }
  if (validationCode !== self.emailValidationKey) {
    debug("Validation Code is wrong");
    err = new Error("Wrong Validation Code for EMail for user >" + self.OSMUser + "<");
    messageCenter.global.sendInfo({oid: self.id, user: user.OSMUser, table: "usert", property: "email", from: null, to: "Validation Failed"}, function() {
      return callback(err);
    });
    return;
  }
  debug("Email Validation OK saving User");
  var oldmail = self.email;
  self.email = self.emailInvalidation;
  delete self.emailInvalidation;
  delete self.emailValidationKey;
  self.save(function logit(err) {
    mailReceiver.updateUser(self);
    if (err) return callback(err);
    messageCenter.global.sendInfo({oid: self.id, user: user.OSMUser, table: "usert", property: "email", from: oldmail, to: self.email}, function() {
      return callback(err);
    });
  });
};

// pgMap setAndSave Function,
// Check on EMail change (and trigger new validation)
// create error if user that already have logged in
// changes their OSM Account
User.prototype.setAndSave = function setAndSave(user, data, callback) {
  debug("setAndSave");
  // reset cache
  util.requireTypes([user, data, callback], ["object", "object", "function"]);
  var self = this;
  delete self.lock;
  var sendWelcomeEmail = false;
  // remove spaces from front and and of email adress
  if (data.email) data.email = data.email.trim();

  // check and react on Mail Change
  if (data.email && data.email.trim() !== "" && data.email !== self.email) {
    if (self.OSMUser !== user.OSMUser && self.hasLoggedIn()) return callback(new Error("EMail address can only be changed by the user himself, after he has logged in."));
    if (data.email !== "resend") {
      if (!emailValidator.validate(data.email)) {
        var error = new Error("Invalid Email Address: " + data.email);
        return callback(error);
      }
      if (data.email !== "") {
        // put email to validation email, and generate a key.
        data.emailInvalidation = data.email;
        data.emailValidationKey = random.generate();
        delete data.email;
      }
    } else {
      // resend case.
      delete data.email;
    }
    sendWelcomeEmail = true;
  }
  // Check Change of OSMUser Name.
  if (data.OSMUser !== self.OSMUser) {
    if (self.hasLoggedIn()) return callback(new Error(">" + self.OSMUser + "< already has logged in, change in name not possible."));
  }
  async.series([
    function checkUserName(cb) {
      if (data.OSMUser && data.OSMUser !== self.OSMUser) {
        find({OSMUser: data.OSMUser}, function(err, result) {
          if (err) return callback(err);
          if (result && result.length) {
            return cb(new Error("User >" + data.OSMUser + "< already exists."));
          } else return cb();
        });
      } else return cb();
    },
    cacheOSMAvatar.bind(null, data.OSMUser)
  ], function finalFunction(err) {
    if (err) return callback(err);
    async.forEachOf(data, function setAndSaveEachOf(value, key, cbEachOf) {
      // There is no Value for the key, so do nothing
      if (typeof (value) === "undefined") return cbEachOf();

      // The Value to be set, is the same then in the object itself
      // so do nothing
      if (value === self[key]) return cbEachOf();
      if (JSON.stringify(value) === JSON.stringify(self[key])) return cbEachOf();
      if (typeof (self[key]) === "undefined" && value === "") return cbEachOf();

      debug("Set Key %s to value >>%s<<", key, value);
      debug("Old Value Was >>%s<<", self[key]);

      async.series([
        function(cb) {
          // do not log validation key in logfile
          var toValue = value;
          // Hide Validation Key not to show to all users
          if (key === "emailValidationKey") return cb();

          messageCenter.global.sendInfo({oid: self.id, user: user.OSMUser, table: "usert", property: key, from: self[key], to: toValue}, cb);
        },
        function(cb) {
          self[key] = value;
          cb();
        }
      ], function(err) {
        cbEachOf(err);
      });
    }, function setAndSaveFinalCB(err) {
      debug("setAndSaveFinalCB");
      if (err) return callback(err);
      self.save(function (err) {
        // Inform Mail Receiver Module, that there could be a change
        if (err) return callback(err);
        // tell mail receiver to update the information about the users
        mailReceiver.updateUser(self);
        if (sendWelcomeEmail) {
          var m = new mailReceiver.MailReceiver(self);
          // do not wait for mail to go out.
          // mail is logged in outgoing mail list
          m.sendWelcomeMail(user.OSMUser, function () {});
        }
        return callback();
      });
    });
  });
};

User.prototype.hasLoggedIn = function hasLoggedIn() {
  debug("User.prototype.hasLoggedIn");
  if (this.lastAccess) return true;
  return false;
};

User.prototype.getNotificationStatus = function getNotificationStatus(channel, type) {
  debug("User.prototype.getNotificationStatus");
  if (!this.notificationStatus) return null;
  if (!this.notificationStatus[channel]) return null;
  return this.notification[channel][type];
};

User.prototype.getMainLang = function getMainLang() {
  debug("User.prototype.getMainLang");
  if (this.mainLang) return this.mainLang;
  if (this.language) return this.language;
  return "EN";
};

User.prototype.getSecondLang = function getSecondLang() {
  debug("User.prototype.getMainLang");
  if (this.secondLang) return this.secondLang;
  return null;
};

User.prototype.getLang3 = function getLang3() {
  debug("User.prototype.getLang3");
  if (this.lang3) return this.lang3;
  return null;
};

User.prototype.getLang4 = function getLang4() {
  debug("User.prototype.getLang4");
  if (this.lang4) return this.lang4;
  return null;
};

User.prototype.setOption = function setOption(view, option, value) {
  debug("User.prototype.setOption");
  if (!this.option) this.option = {};
  if (!this.option[view]) this.option[view] = {};
  this.option[view][option] = value;
};

var defaultOption = {};

User.prototype.getOption = function getOption(view, option) {
  debug("User.protoype.getOption");
  if (this.option && this.option[view] && this.option[view][option]) return this.option[view][option];
  if (defaultOption && defaultOption[view] && defaultOption[view][option]) return defaultOption[view][option];
  return null;
};

User.prototype.createApiKey = function createApiKey(callback) {
  debug("createApiKey");
  let apiKey = random.generate({length: 10});
  this.apiKey = apiKey;
  this.save(callback);
};

// this functions returns (and caches) the new users
// Users that have done their first edit in the last month

var _newUsers = null;
let interval = config.getValue("WelcomeInterval", {mustExist: true});

module.exports.getNewUsers = function getNewUsers(callback) {
  debug("getNewUsers");
  if (_newUsers) return callback(null, _newUsers);

  pgMap.select("select data->>'user' as osmuser ,min(data->>'timestamp') as first from changes group by data->>'user' having ( min(data->>'timestamp')  )::timestamp with time zone  > current_timestamp - interval '" + interval + "'", function(err, result) {
    if (err) return callback(err);
    _newUsers = result;
    setTimeout(function() {
      _newUsers = null;
    }, 10 * 60 * 1000);
    callback(null, result);
  });
};

// Creates an User object and stores it to database
// can use a prototype to initialise data
// Parameter: Prototype (optional)
//            callback
// Prototype is not allowed to have an id
module.exports.createNewUser = createNewUser;

// save stores the current object to database
User.prototype.save = pgMap.save; // Create Tables and Views

module.exports.create = create;
module.exports.find = find;
module.exports.findById = findById;
module.exports.findOne = findOne;

User.prototype.getTable = function getTable() {
  return "usert";
};
<|endoftext|>


<commit_before>import React from "react";
import { defineMessages, injectIntl, intlShape, FormattedMessage } from "react-intl";
import PropTypes from "prop-types";
import { Tabs, Tab } from "patternfly-react";
import ListView from "./ListView";
import ListItemComponents from "./ListItemComponents";
import DependencyListView from "./DependencyListView";
import EmptyState from "../EmptyState/EmptyState";
import Loading from "../Loading/Loading";
import LabelWithBadge from "./LabelWithBadge";

const messages = defineMessages({
  dependenciesTabTitle: {
    defaultMessage: "Dependencies"
  },
  emptyStateErrorMessage: {
    defaultMessage: "An error occurred while trying to get blueprint contents."
  },
  emptyStateErrorTitle: {
    defaultMessage: "An Error Occurred"
  },
  emptyStateNoResultsMessage: {
    defaultMessage: "Modify your filter criteria to get results."
  },
  emptyStateNoResultsTitle: {
    defaultMessage: "No Results Match the Filter Criteria"
  },
  selectedTabTitle: {
    defaultMessage: "Selected Components"
  }
});

const BlueprintContents = props => {
  const {
    components,
    dependencies,
    handleComponentDetails,
    handleRemoveComponent,
    noEditComponent,
    filterClearValues,
    filterValues,
    errorState,
    fetchingState,
    fetchDetails,
    pastLength,
    undo
  } = props;

  const { formatMessage } = props.intl;

  return (

      {(fetchingState === true && <Loading />) ||
        ((Object.keys(errorState).length > 0 && (
          <EmptyState
            title={formatMessage(messages.emptyStateErrorTitle)}
            message={formatMessage(messages.emptyStateErrorMessage)}
          >

{errorState.msg}

            {pastLength > 0 && (
              <button className="btn btn-link btn-lg" type="button" onClick={() => undo()}>
                <FormattedMessage defaultMessage="Undo Last Change" />
              </button>
            )}
          </EmptyState>
        )) ||
          ((components.length === 0 && filterValues.length === 0 &&
{props.children}
) || (
            <Tabs id="blueprint-tabs">
              <Tab
                eventKey="selected-components"
                title={<LabelWithBadge title={formatMessage(messages.selectedTabTitle)} badge={components.length} />}
              >
                {(components.length === 0 && (
                  <EmptyState
                    title={formatMessage(messages.emptyStateNoResultsTitle)}
                    message={formatMessage(messages.emptyStateNoResultsMessage)}
                  >
                    <button className="btn btn-link btn-lg" type="button" onClick={() => filterClearValues([])}>
                      <FormattedMessage defaultMessage="Clear All Filters" />
                    </button>
                  </EmptyState>
                )) || (
                  <ListView className="cmpsr-blueprint__components" stacked>
                    {components.map(listItem => (
                      <ListItemComponents
                        listItemParent="cmpsr-blueprint__components"
                        listItem={listItem}
                        key={listItem.name}
                        handleRemoveComponent={handleRemoveComponent}
                        handleComponentDetails={handleComponentDetails}
                        noEditComponent={noEditComponent}
                        fetchDetails={fetchDetails}
                      />
                    ))}
                  </ListView>
                )}
              </Tab>
              <Tab
                eventKey="dependencies"
                title={
                  <LabelWithBadge title={formatMessage(messages.dependenciesTabTitle)} badge={dependencies.length} />
                }
              >
                {(dependencies.length === 0 && (
                  <EmptyState
                    title={formatMessage(messages.emptyStateNoResultsTitle)}
                    message={formatMessage(messages.emptyStateNoResultsMessage)}
                  >
                    <button className="btn btn-link btn-lg" type="button" onClick={() => filterClearValues([])}>
                      <FormattedMessage defaultMessage="Clear All Filters" />
                    </button>
                  </EmptyState>
                )) || (
                  <DependencyListView
                    className="cmpsr-blueprint__dependencies"
                    listItems={dependencies}
                    handleRemoveComponent={handleRemoveComponent}
                    handleComponentDetails={handleComponentDetails}
                    noEditComponent={noEditComponent}
                    fetchDetails={fetchDetails}
                  />
                )}
              </Tab>
            </Tabs>
          )))}

  );
};

BlueprintContents.propTypes = {
  components: PropTypes.arrayOf(PropTypes.object),
  dependencies: PropTypes.arrayOf(PropTypes.object),
  handleComponentDetails: PropTypes.func,
  handleRemoveComponent: PropTypes.func,
  intl: intlShape.isRequired,
  noEditComponent: PropTypes.bool,
  filterClearValues: PropTypes.func,
  filterValues: PropTypes.arrayOf(PropTypes.object),
  errorState: PropTypes.shape({
    message: PropTypes.string,
    options: PropTypes.object,
    problem: PropTypes.string,
    url: PropTypes.string
  }),
  fetchingState: PropTypes.bool,
  fetchDetails: PropTypes.func,
  children: PropTypes.node,
  pastLength: PropTypes.number,
  undo: PropTypes.func
};

BlueprintContents.defaultProps = {
  components: [],
  dependencies: [],
  handleComponentDetails: function() {},
  handleRemoveComponent: function() {},
  noEditComponent: false,
  filterClearValues: function() {},
  filterValues: [],
  errorState: {},
  fetchingState: false,
  fetchDetails: function() {},
  children: React.createElement("div"),
  pastLength: 0,
  undo: function() {}
};

export default injectIntl(BlueprintContents);
<commit_msg>Fix proptype for error message<commit_after>import React from "react";
import { defineMessages, injectIntl, intlShape, FormattedMessage } from "react-intl";
import PropTypes from "prop-types";
import { Tabs, Tab } from "patternfly-react";
import ListView from "./ListView";
import ListItemComponents from "./ListItemComponents";
import DependencyListView from "./DependencyListView";
import EmptyState from "../EmptyState/EmptyState";
import Loading from "../Loading/Loading";
import LabelWithBadge from "./LabelWithBadge";

const messages = defineMessages({
  dependenciesTabTitle: {
    defaultMessage: "Dependencies"
  },
  emptyStateErrorMessage: {
    defaultMessage: "An error occurred while trying to get blueprint contents."
  },
  emptyStateErrorTitle: {
    defaultMessage: "An Error Occurred"
  },
  emptyStateNoResultsMessage: {
    defaultMessage: "Modify your filter criteria to get results."
  },
  emptyStateNoResultsTitle: {
    defaultMessage: "No Results Match the Filter Criteria"
  },
  selectedTabTitle: {
    defaultMessage: "Selected Components"
  }
});

const BlueprintContents = props => {
  const {
    components,
    dependencies,
    handleComponentDetails,
    handleRemoveComponent,
    noEditComponent,
    filterClearValues,
    filterValues,
    errorState,
    fetchingState,
    fetchDetails,
    pastLength,
    undo
  } = props;

  const { formatMessage } = props.intl;

  return (

      {(fetchingState === true && <Loading />) ||
        ((Object.keys(errorState).length > 0 && (
          <EmptyState
            title={formatMessage(messages.emptyStateErrorTitle)}
            message={formatMessage(messages.emptyStateErrorMessage)}
          >

{errorState.msg}

            {pastLength > 0 && (
              <button className="btn btn-link btn-lg" type="button" onClick={() => undo()}>
                <FormattedMessage defaultMessage="Undo Last Change" />
              </button>
            )}
          </EmptyState>
        )) ||
          ((components.length === 0 && filterValues.length === 0 &&
{props.children}
) || (
            <Tabs id="blueprint-tabs">
              <Tab
                eventKey="selected-components"
                title={<LabelWithBadge title={formatMessage(messages.selectedTabTitle)} badge={components.length} />}
              >
                {(components.length === 0 && (
                  <EmptyState
                    title={formatMessage(messages.emptyStateNoResultsTitle)}
                    message={formatMessage(messages.emptyStateNoResultsMessage)}
                  >
                    <button className="btn btn-link btn-lg" type="button" onClick={() => filterClearValues([])}>
                      <FormattedMessage defaultMessage="Clear All Filters" />
                    </button>
                  </EmptyState>
                )) || (
                  <ListView className="cmpsr-blueprint__components" stacked>
                    {components.map(listItem => (
                      <ListItemComponents
                        listItemParent="cmpsr-blueprint__components"
                        listItem={listItem}
                        key={listItem.name}
                        handleRemoveComponent={handleRemoveComponent}
                        handleComponentDetails={handleComponentDetails}
                        noEditComponent={noEditComponent}
                        fetchDetails={fetchDetails}
                      />
                    ))}
                  </ListView>
                )}
              </Tab>
              <Tab
                eventKey="dependencies"
                title={
                  <LabelWithBadge title={formatMessage(messages.dependenciesTabTitle)} badge={dependencies.length} />
                }
              >
                {(dependencies.length === 0 && (
                  <EmptyState
                    title={formatMessage(messages.emptyStateNoResultsTitle)}
                    message={formatMessage(messages.emptyStateNoResultsMessage)}
                  >
                    <button className="btn btn-link btn-lg" type="button" onClick={() => filterClearValues([])}>
                      <FormattedMessage defaultMessage="Clear All Filters" />
                    </button>
                  </EmptyState>
                )) || (
                  <DependencyListView
                    className="cmpsr-blueprint__dependencies"
                    listItems={dependencies}
                    handleRemoveComponent={handleRemoveComponent}
                    handleComponentDetails={handleComponentDetails}
                    noEditComponent={noEditComponent}
                    fetchDetails={fetchDetails}
                  />
                )}
              </Tab>
            </Tabs>
          )))}

  );
};

BlueprintContents.propTypes = {
  components: PropTypes.arrayOf(PropTypes.object),
  dependencies: PropTypes.arrayOf(PropTypes.object),
  handleComponentDetails: PropTypes.func,
  handleRemoveComponent: PropTypes.func,
  intl: intlShape.isRequired,
  noEditComponent: PropTypes.bool,
  filterClearValues: PropTypes.func,
  filterValues: PropTypes.arrayOf(PropTypes.object),
  errorState: PropTypes.shape({
    message: PropTypes.string,
    options: PropTypes.object,
    problem: PropTypes.string,
    url: PropTypes.string
  }),
  fetchingState: PropTypes.bool,
  fetchDetails: PropTypes.func,
  children: PropTypes.oneOfType([PropTypes.element, PropTypes.arrayOf(PropTypes.element)]),
  pastLength: PropTypes.number,
  undo: PropTypes.func
};

BlueprintContents.defaultProps = {
  components: [],
  dependencies: [],
  handleComponentDetails: function() {},
  handleRemoveComponent: function() {},
  noEditComponent: false,
  filterClearValues: function() {},
  filterValues: [],
  errorState: {},
  fetchingState: false,
  fetchDetails: function() {},
  children: React.createElement("div"),
  pastLength: 0,
  undo: function() {}
};

export default injectIntl(BlueprintContents);
<|endoftext|>