<commit_before>( function() {
  'use strict'

  var GE = Gibber.Environment

  var Column = function( options, Layout ) {
    options = options || {}

    var isCodeColumn = options.type === 'code',
        lastColumnWidth = 0,
        colNumber = Layout.columns.length,
        mode  = 'javascript',
        modeIndex = 0,
        columnWidth = options.width ? options.width : Layout.defaultColumnSize,
        col = this,
        resizeHandleSize = Layout.resizeHandleSize

    $.extend( this, {
      element:        $( '
' ),
      header:         $( '
' ),
      modeSelect:     $( '<select>' ),
      bodyElement:    $( '
' ),
      resizeHandle:   $( '
' ),
      close :         $( '<button>' ),
      width:          columnWidth,
      number:         Layout.columns.length,
      fontSize:       1,
      modeIndex:      0,
      isCodeColumn:   isCodeColumn,
      isFullScreen:   false,
      resizeHandleSize  : 8,

      // fullScreen:     this.makeFullScreenFunction(),
    })

    Layout.columns.push( col )

    Object.defineProperty( col, 'value', {
      get: function()    { return col.editor.getValue() },
      set: function( v ) { col.editor.setValue( v ) }
    })

    col.element.width( columnWidth )
    col.resizeHandle.width( resizeHandleSize )

    col.close.addClass( 'closeButton' )
      .on( 'click', function(e) { Layout.removeColumn( colNumber );  if( col.onclose ) col.onclose(); })
      .css({ fontSize:'.8em', borderRight:'1px solid #666', padding:'.25em', fontWeight:'bold' })
      .html( '✕' )
      .attr( 'title', 'close column' )

    if( isCodeColumn ) {
      for( var key in GE.modes ) {
        if( key !== 'nameMappings' ) {
          col.modeSelect.append( $( '<option>' ).text( key ) )
        }
      }
      col.modeSelect
        .eq( 0 )
        .on( 'change', function( e ) {
          var opt = $( this ).find( ':selected' ), idx = opt.index(), val = opt.text()

          col.modeIndex = idx
					col.mode = val
          col.editor.setOption( 'mode', GE.modes.nameMappings[ col.mode ] )

					col.editor.setValue( GE.modes[ col.mode ].default )
        })
        .attr( 'title', 'set language for column' )

      col.header
        .append( col.close )
        .append( $( '' ).html( ' id #: ' + colNumber + '   language:' ) )
        .append( col.modeSelect )

    }else{
      col.header
        .append( col.close )
        .append( $( '' ).html( ' ' + (options.header || '') ) )
    }

    col.element.append( col.header, col.resizeHandle )

    $( '#contentCell' ).append( col.element )

    if( typeof options.mode === 'string' ) {
      mode = options.mode
    }else if( options.mode ) {
      mode = modes[ options.mode ]
    }

    var shouldDisplayLoadFile = typeof window.loadFile !== 'undefined' && window.loadFile !== null && typeof window.loadFile.error === 'undefined' && Layout.columns.length === 1, // make sure it's only on the first load
        _value = shouldDisplayLoadFile ? window.loadFile.text  :  GE.modes[ mode ].default;

    if( isCodeColumn ) {
      col.bodyElement.width( columnWidth - resizeHandleSize )
      col.element.append( col.bodyElement )
      col.editor = CodeMirror( col.bodyElement[0], {
        theme:  'gibber',
        keyMap: 'gibber',
        mode:   mode !== 'javascript' ? 'x-shader/x-fragment' : 'javascript',
        autoCloseBrackets: true,
        matchBrackets: true,
        value: _value,
        lineWrapping: false,
        tabSize: 2,
        lineNumbers:false,
        // cursorBlinkRate: 530,
        autofocus: options.autofocus || false,
      })

      col.editor.on( 'mousedown', function( cm, e ) {
        //console.log( e.toElement.className )
        var classes = e.toElement.className.split(' ')

        for( var i = 0; i < classes.length; i++ ) {
          if( cm.listeners[ classes[ i ] ] ) {
            cm.listeners[ classes[ i ] ]( e )
          }
        }
      })
      col.editor.on('drop', function (cm, e) { e.preventDefault(); })

      col.lineNumbersButton = $( '<button>' ).text('#')
        .on( 'click', function( e ) {
          col.editor.setOption( 'lineNumbers', !col.editor.getOption('lineNumbers') )
        })
        .addClass( 'lineNumbersButton' )
        .attr( 'title', 'toggle line numbers' )

      col.fileInfoButton = $( '<button>' ).text('?')
        .on( 'click', function( e ) {
          col.showFileInfo()
        })
        .addClass( 'lineNumbersButton' )
        .attr( 'title', 'show file info' )

      col.infoDiv = null
      col.header.append( col.lineNumbersButton, col.fileInfoButton )
      col.editor.column = col
      col.editor.on('focus', function() {
        Layout.focusedColumn = colNumber
      })

      col.editor.listeners = {}

      // remove event handlers on clearing Gibber
      $.subscribe('/gibber/clear', function() {
        col.editor.listeners = {}
      })

    }else{
      col.bodyElement.width( columnWidth - resizeHandleSize )
      col.element.append( col.bodyElement )
    }

    col.modeIndex = typeof mode === 'undefined' || mode === 'javascript' ? 0 : 1;
    col.modeSelect.eq( col.modeIndex )
    col.modeSelect.addClass( 'modeSelectDropDown' )

    if( isCodeColumn )
      $( col.modeSelect ).find( 'option' )[ col.modeIndex ].selected = true;

		col.mode = 'javascript'
    col.element.addClass( colNumber )
    col.element.attr( 'id', colNumber )
    col.id = colNumber

    Layout.handleResizeEventForColumn( col )

    Layout.resizeColumns()

    $( 'html,body' ).animate({ scrollLeft: $( '#' + col.id ).offset().left }, 'slow' );

    if( window.loadFile && window.loadFile.error && Layout.columns.length === 1 ) {
      console.log( window.loadFile )
      GE.Message.post('You attempted to load a document that does not exist. Please check the URL you entered and try again.')
    }
  }

  Gibber.Environment.Layout.Column = Column

  $.extend( Column.prototype, {
    toggle:             function() { $( this.element ).toggle() },
    toggleResizeHandle: function() { $( this.element ).find( '.resizeHandle' ).toggle() },

    fullScreen : function() {
      if( GE.Layout.fullScreenColumn === null ) {
        GE.Layout.toggle()

        GE.Layout.__fullScreenColumn__.toggle()
        GE.Layout.__fullScreenColumn__.editor.focus()
        GE.Layout.__fullScreenColumn__.editor.setValue( this.editor.getValue() )

        GE.Layout.fullScreenColumn = this
        if( Graphics.running ) Graphics.assignWidthAndHeight()
        GE.Layout.isFullScreen = true
      }else{
        GE.Layout.toggle()
        GE.Layout.__fullScreenColumn__.toggle()
        this.editor.setValue( GE.Layout.__fullScreenColumn__.editor.getValue() )
        GE.Layout.__fullScreenColumn__.editor.setValue( '' )

        this.editor.focus()

        GE.Layout.fullScreenColumn = null
        if( Graphics.running ) Graphics.assignWidthAndHeight()
        GE.Layout.isFullScreen = false
      }
    },

    makeFullScreenFunction : function() {
      var _w = null, _h = null, fnc

      fnc = function() {
        //console.log( this )
        if( !this.isFullScreen ) {
          if( !Layout.isFullScreen ) {
            Layout.fullScreen()
          }
          _w = this.width - Layout.resizeHandleSize
          _h = this.bodyElement.innerHeight
          var w = $( window ).width(), h = $( window ).height()
          this.toggle()
          this.header.hide()
          this.toggleResizeHandle()
          this.element.css({ width: w, height: h, top:0, left:0 })
          this.bodyElement.css({ width: w, height: h })
          this.editor.setSize( w,h )
          this.isFullScreen = true
          GE.Layout.fullScreenColumn = this
        }else{
          if( Layout.isFullScreen ) {
            Layout.fullScreen()
          }
          this.toggle()
          this.header.show()
          this.toggleResizeHandle()
          this.element.css({ width: _w, top:31 })
          this.bodyElement.css({ width: _w, height:_h })
          this.editor.setSize( _w, _h )
          Layout.resizeColumns()
          this.isFullScreen = false
          GE.Layout.fullScreenColumn = null
        }
      }
      return fnc
    },

		setMode : function(mode) {
			this.mode = mode
      this.editor.setOption( 'mode', GE.modes.nameMappings[ this.mode ] )
      $( this.modeSelect ).val( this.mode )
			this.editor.setValue( GE.modes[ this.mode ].default )
		},

    save : function() {
      var col = this
      //    updateDocument : function( revisions, previous, notes, column ) {
      if( this.fileInfo && this.value !== this.fileInfo.text ) {
        GE.Account.updateDocument({ text: this.value }, this.fileInfo, '', this )
      }else{
        if( !this.fileInfo ) {
          GE.Message.post( 'You need to publish this file before you can save it. The publish button is at the top of the Gibber menubar.')
        }else if( this.value === this.fileInfo.text ) {
          GE.Message.post( 'The current text is the same as what is in the database; no update was performed.')
        }
      }
    },

    setWidth: function( w ) {
      var newWidthCandidate = w
      this.width = newWidthCandidate > 300 ? newWidthCandidate : 300
      this.element.width( this.width )

      GE.Layout.resizeColumns()
    },

    showFileInfo : function() {
      var html, table, col = this

      if( this.infoDiv !== null ) return

      $.extend( this, {
        infoDiv : $('
').css({
          height:this.bodyElement.innerHeight(),
          width:this.bodyElement.innerWidth(),
          position:'absolute',
          top:this.header.height(),
          left:0,
          display:'block',
          background:'rgba(0,0,0,.8)',
          color:'#aaa',
          zIndex:10
        }),

        infoDivClose : $( '<button>')
          .addClass( 'closeButton' )
          .on( 'click', function(e) { col.infoDiv.remove(); col.infoDiv = null; })
          .css({
            fontSize:'1em',
            display:'inline',
            border:'1px solid #666',
            padding:'.25em',
            background:'#191919',
            width:'80%',
            marginLeft:'10%',
            fontFamily:'Helvetica, sans-serif',
            '-moz-box-sizing': 'border-box !important',
            'box-sizing': 'border-box !important'
          })
          .html( 'close file information view' )
          .attr( 'title', 'close file info view' ),
      })

      var html
      if( !this.fileInfo ) {
        html = "
This file has not been published.

If you publish the file, you will see authorship information here and a revision history.
"
        this.infoDiv.append( $( html ).css({ margin:'1em' }) )
        this.infoDiv.append( this.infoDivClose )
        this.bodyElement.prepend( this.infoDiv )

        return
      }

      html = [
        "
",
        "

 " + this.fileInfo.name + "

",
        "
author: " + this.fileInfo.author + "
",
        "
tags: " + (this.fileInfo.tags || 'none') + "
",
        "
notes: " + (this.fileInfo.notes || 'none') + "
",
        "
"
      ].join('\n')

      table = $( html ).css({ margin:'1em' })
      //$( $( $( table ).find( 'tr' )[0] ).find('td')[0] ).append( col.infoDivClose )
      //console.log( "FILE INFO", col.fileInfo, col.fileInfo._revs_info.length )
      if( this.fileInfo._revs_info.length > 1 ) {
        var list = $( '
' ), tr, td, a

        list.append( $('
').html('revisions') )

        for( var i = 0; i < this.fileInfo._revs_info.length; i++ ) {
          ( function() {
            var _col = col, li = $( '
' ).text( this.fileInfo._revs_info[ i ].rev )
              .on('click', ( function() {
                var rev = _col.fileInfo.author + '/publications/' + _col.fileInfo.name + '?rev=' + _col.fileInfo._revs_info[ i ].rev
                var fnc = function() {
                  GE.Browser.openCode( rev )
                }
                return fnc
              })()
              )
              .css({ cursor:'pointer', color:'#aaa' })
              .hover( function() { $( li ).css({ color:'#fff', textDecoration:'underline' } )}, function() { $( li ).css({ color:'#aaa', textDecoration:'none'} )})

            list.append( li )
          })()
        }

        td = $('').append( list )
        tr = $('
').append( td )

        table.append( tr )
      }

      table.find( 'td' ).css({ paddingBottom:'1em' })

      this.infoDiv.append( table )
      this.infoDiv.append( this.infoDivClose )
      this.bodyElement.prepend( this.infoDiv )
    }
  })
})()<commit_msg>important fix for viewing column file info<commit_after>( function() {
  'use strict'

  var GE = Gibber.Environment

  var Column = function( options, Layout ) {
    options = options || {}

    var isCodeColumn = options.type === 'code',
        lastColumnWidth = 0,
        colNumber = Layout.columns.length,
        mode  = 'javascript',
        modeIndex = 0,
        columnWidth = options.width ? options.width : Layout.defaultColumnSize,
        col = this,
        resizeHandleSize = Layout.resizeHandleSize

    $.extend( this, {
      element:        $( '
' ),
      header:         $( '
' ),
      modeSelect:     $( '<select>' ),
      bodyElement:    $( '
' ),
      resizeHandle:   $( '
' ),
      close :         $( '<button>' ),
      width:          columnWidth,
      number:         Layout.columns.length,
      fontSize:       1,
      modeIndex:      0,
      isCodeColumn:   isCodeColumn,
      isFullScreen:   false,
      resizeHandleSize  : 8,

      // fullScreen:     this.makeFullScreenFunction(),
    })

    Layout.columns.push( col )

    Object.defineProperty( col, 'value', {
      get: function()    { return col.editor.getValue() },
      set: function( v ) { col.editor.setValue( v ) }
    })

    col.element.width( columnWidth )
    col.resizeHandle.width( resizeHandleSize )

    col.close.addClass( 'closeButton' )
      .on( 'click', function(e) { Layout.removeColumn( colNumber );  if( col.onclose ) col.onclose(); })
      .css({ fontSize:'.8em', borderRight:'1px solid #666', padding:'.25em', fontWeight:'bold' })
      .html( '✕' )
      .attr( 'title', 'close column' )

    if( isCodeColumn ) {
      for( var key in GE.modes ) {
        if( key !== 'nameMappings' ) {
          col.modeSelect.append( $( '<option>' ).text( key ) )
        }
      }
      col.modeSelect
        .eq( 0 )
        .on( 'change', function( e ) {
          var opt = $( this ).find( ':selected' ), idx = opt.index(), val = opt.text()

          col.modeIndex = idx
					col.mode = val
          col.editor.setOption( 'mode', GE.modes.nameMappings[ col.mode ] )

					col.editor.setValue( GE.modes[ col.mode ].default )
        })
        .attr( 'title', 'set language for column' )

      col.header
        .append( col.close )
        .append( $( '' ).html( ' id #: ' + colNumber + '   language:' ) )
        .append( col.modeSelect )

    }else{
      col.header
        .append( col.close )
        .append( $( '' ).html( ' ' + (options.header || '') ) )
    }

    col.element.append( col.header, col.resizeHandle )

    $( '#contentCell' ).append( col.element )

    if( typeof options.mode === 'string' ) {
      mode = options.mode
    }else if( options.mode ) {
      mode = modes[ options.mode ]
    }

    var shouldDisplayLoadFile = typeof window.loadFile !== 'undefined' && window.loadFile !== null && typeof window.loadFile.error === 'undefined' && Layout.columns.length === 1, // make sure it's only on the first load
        _value = shouldDisplayLoadFile ? window.loadFile.text  :  GE.modes[ mode ].default;

    if( isCodeColumn ) {
      col.bodyElement.width( columnWidth - resizeHandleSize )
      col.element.append( col.bodyElement )
      col.editor = CodeMirror( col.bodyElement[0], {
        theme:  'gibber',
        keyMap: 'gibber',
        mode:   mode !== 'javascript' ? 'x-shader/x-fragment' : 'javascript',
        autoCloseBrackets: true,
        matchBrackets: true,
        value: _value,
        lineWrapping: false,
        tabSize: 2,
        lineNumbers:false,
        // cursorBlinkRate: 530,
        autofocus: options.autofocus || false,
      })

      col.editor.on( 'mousedown', function( cm, e ) {
        //console.log( e.toElement.className )
        var classes = e.toElement.className.split(' ')

        for( var i = 0; i < classes.length; i++ ) {
          if( cm.listeners[ classes[ i ] ] ) {
            cm.listeners[ classes[ i ] ]( e )
          }
        }
      })
      col.editor.on('drop', function (cm, e) { e.preventDefault(); })

      col.lineNumbersButton = $( '<button>' ).text('#')
        .on( 'click', function( e ) {
          col.editor.setOption( 'lineNumbers', !col.editor.getOption('lineNumbers') )
        })
        .addClass( 'lineNumbersButton' )
        .attr( 'title', 'toggle line numbers' )

      col.fileInfoButton = $( '<button>' ).text('?')
        .on( 'click', function( e ) {
          col.showFileInfo()
        })
        .addClass( 'lineNumbersButton' )
        .attr( 'title', 'show file info' )

      col.infoDiv = null
      col.header.append( col.lineNumbersButton, col.fileInfoButton )
      col.editor.column = col
      col.editor.on('focus', function() {
        Layout.focusedColumn = colNumber
      })

      col.editor.listeners = {}

      // remove event handlers on clearing Gibber
      $.subscribe('/gibber/clear', function() {
        col.editor.listeners = {}
      })

    }else{
      col.bodyElement.width( columnWidth - resizeHandleSize )
      col.element.append( col.bodyElement )
    }

    col.modeIndex = typeof mode === 'undefined' || mode === 'javascript' ? 0 : 1;
    col.modeSelect.eq( col.modeIndex )
    col.modeSelect.addClass( 'modeSelectDropDown' )

    if( isCodeColumn )
      $( col.modeSelect ).find( 'option' )[ col.modeIndex ].selected = true;

		col.mode = 'javascript'
    col.element.addClass( colNumber )
    col.element.attr( 'id', colNumber )
    col.id = colNumber

    Layout.handleResizeEventForColumn( col )

    Layout.resizeColumns()

    $( 'html,body' ).animate({ scrollLeft: $( '#' + col.id ).offset().left }, 'slow' );

    if( window.loadFile && window.loadFile.error && Layout.columns.length === 1 ) {
      console.log( window.loadFile )
      GE.Message.post('You attempted to load a document that does not exist. Please check the URL you entered and try again.')
    }
  }

  Gibber.Environment.Layout.Column = Column

  $.extend( Column.prototype, {
    toggle:             function() { $( this.element ).toggle() },
    toggleResizeHandle: function() { $( this.element ).find( '.resizeHandle' ).toggle() },

    fullScreen : function() {
      if( GE.Layout.fullScreenColumn === null ) {
        GE.Layout.toggle()

        GE.Layout.__fullScreenColumn__.toggle()
        GE.Layout.__fullScreenColumn__.editor.focus()
        GE.Layout.__fullScreenColumn__.editor.setValue( this.editor.getValue() )

        GE.Layout.fullScreenColumn = this
        if( Graphics.running ) Graphics.assignWidthAndHeight()
        GE.Layout.isFullScreen = true
      }else{
        GE.Layout.toggle()
        GE.Layout.__fullScreenColumn__.toggle()
        this.editor.setValue( GE.Layout.__fullScreenColumn__.editor.getValue() )
        GE.Layout.__fullScreenColumn__.editor.setValue( '' )

        this.editor.focus()

        GE.Layout.fullScreenColumn = null
        if( Graphics.running ) Graphics.assignWidthAndHeight()
        GE.Layout.isFullScreen = false
      }
    },

    makeFullScreenFunction : function() {
      var _w = null, _h = null, fnc

      fnc = function() {
        //console.log( this )
        if( !this.isFullScreen ) {
          if( !Layout.isFullScreen ) {
            Layout.fullScreen()
          }
          _w = this.width - Layout.resizeHandleSize
          _h = this.bodyElement.innerHeight
          var w = $( window ).width(), h = $( window ).height()
          this.toggle()
          this.header.hide()
          this.toggleResizeHandle()
          this.element.css({ width: w, height: h, top:0, left:0 })
          this.bodyElement.css({ width: w, height: h })
          this.editor.setSize( w,h )
          this.isFullScreen = true
          GE.Layout.fullScreenColumn = this
        }else{
          if( Layout.isFullScreen ) {
            Layout.fullScreen()
          }
          this.toggle()
          this.header.show()
          this.toggleResizeHandle()
          this.element.css({ width: _w, top:31 })
          this.bodyElement.css({ width: _w, height:_h })
          this.editor.setSize( _w, _h )
          Layout.resizeColumns()
          this.isFullScreen = false
          GE.Layout.fullScreenColumn = null
        }
      }
      return fnc
    },

		setMode : function(mode) {
			this.mode = mode
      this.editor.setOption( 'mode', GE.modes.nameMappings[ this.mode ] )
      $( this.modeSelect ).val( this.mode )
			this.editor.setValue( GE.modes[ this.mode ].default )
		},

    save : function() {
      var col = this
      //    updateDocument : function( revisions, previous, notes, column ) {
      if( this.fileInfo && this.value !== this.fileInfo.text ) {
        GE.Account.updateDocument({ text: this.value }, this.fileInfo, '', this )
      }else{
        if( !this.fileInfo ) {
          GE.Message.post( 'You need to publish this file before you can save it. The publish button is at the top of the Gibber menubar.')
        }else if( this.value === this.fileInfo.text ) {
          GE.Message.post( 'The current text is the same as what is in the database; no update was performed.')
        }
      }
    },

    setWidth: function( w ) {
      var newWidthCandidate = w
      this.width = newWidthCandidate > 300 ? newWidthCandidate : 300
      this.element.width( this.width )

      GE.Layout.resizeColumns()
    },

    showFileInfo : function() {
      var html, table, col = this

      if( this.infoDiv !== null ) return

      $.extend( this, {
        infoDiv : $('
').css({
          height:this.bodyElement.innerHeight(),
          width:this.bodyElement.innerWidth(),
          position:'absolute',
          top:this.header.height(),
          left:0,
          display:'block',
          background:'rgba(0,0,0,.8)',
          color:'#aaa',
          zIndex:10
        }),

        infoDivClose : $( '<button>')
          .addClass( 'closeButton' )
          .on( 'click', function(e) { col.infoDiv.remove(); col.infoDiv = null; })
          .css({
            fontSize:'1em',
            display:'inline',
            border:'1px solid #666',
            padding:'.25em',
            background:'#191919',
            width:'80%',
            marginLeft:'10%',
            fontFamily:'Helvetica, sans-serif',
            '-moz-box-sizing': 'border-box !important',
            'box-sizing': 'border-box !important'
          })
          .html( 'close file information view' )
          .attr( 'title', 'close file info view' ),
      })

      var html
      if( !this.fileInfo ) {
        html = "
This file has not been published.

If you publish the file, you will see authorship information here and a revision history.
"
        this.infoDiv.append( $( html ).css({ margin:'1em' }) )
        this.infoDiv.append( this.infoDivClose )
        this.bodyElement.prepend( this.infoDiv )

        return
      }

      html = [
        "
",
        "

 " + this.fileInfo.name + "

",
        "
author: " + this.fileInfo.author + "
",
        "
tags: " + (this.fileInfo.tags || 'none') + "
",
        "
notes: " + (this.fileInfo.notes || 'none') + "
",
        "
"
      ].join('\n')

      table = $( html ).css({ margin:'1em' })
      //$( $( $( table ).find( 'tr' )[0] ).find('td')[0] ).append( col.infoDivClose )
      //console.log( "FILE INFO", col.fileInfo, col.fileInfo._revs_info.length )
      if( this.fileInfo._revs_info.length > 1 ) {
        var list = $( '
' ), tr, td, a

        list.append( $('
').html('revisions') )

        for( var i = 0; i < this.fileInfo._revs_info.length; i++ ) {
          ( function() {
            var _col = col, li = $( '
' ).text( _col.fileInfo._revs_info[ i ].rev )
              .on('click', ( function() {
                var rev = _col.fileInfo.author + '/publications/' + _col.fileInfo.name + '?rev=' + _col.fileInfo._revs_info[ i ].rev
                var fnc = function() {
                  GE.Browser.openCode( rev )
                }
                return fnc
              })()
              )
              .css({ cursor:'pointer', color:'#aaa' })
              .hover( function() { $( li ).css({ color:'#fff', textDecoration:'underline' } )}, function() { $( li ).css({ color:'#aaa', textDecoration:'none'} )})

            list.append( li )
          })()
        }

        td = $('').append( list )
        tr = $('
').append( td )

        table.append( tr )
      }

      table.find( 'td' ).css({ paddingBottom:'1em' })

      this.infoDiv.append( table )
      this.infoDiv.append( this.infoDivClose )
      this.bodyElement.prepend( this.infoDiv )
    }
  })
})()<|endoftext|>


<commit_before>(function() {

  var map;
  var ghLayer;
  var heat;
  var bands = 5;
  var maxRate = 0;
  var viewMode = "markers";
  var ghGeojsonUrl = "data/github-cities.geojson";

  var heatmapPoints = [];

  var icons = [
      [25, 23, "imgs/github.png"],
      [35, 33, "imgs/redpolo.png"],
      [45, 43, "imgs/americanfootball.png"],
      [55, 53, "imgs/jetpack.png"],
      [65, 63, "imgs/professor.png"]
  ];

  init();

  function init() {

    var center = [ 54.514, -2.122];
    var zoom = 6;
    map = L.map('map').setView(center, zoom);
    L.esri.basemapLayer("Gray").addTo(map);
    L.esri.basemapLayer("GrayLabels").addTo(map);

    initMarkers();
    initListners();

  }

  function initListners() {

    map.on('zoomend', setMarkerOpacity);
    document.getElementById("switch").addEventListener("click", toggleRenderer);

  }

  function initLegend() {

    var legend = L.control({position: 'bottomleft'});
    legend.onAdd = function (map) {

        var div = L.DomUtil.create('div', 'legend');
        var legendTitle = ' % of Pop. with GitHub';

        //Loop through our density intervals and generate a label with a colored square for each interval
        var lowerBounds = "0.000";
        var upperBounds;
        var lines = "";

        for (var i = 0; i < bands; i++) {
            upperBounds = ((maxRate / bands) * (i + 1)).toFixed(3);
            bounds = lowerBounds+' - '+upperBounds;
            lines = '
'+bounds+'' +
                    '
' + lines;
            lowerBounds = (parseFloat(upperBounds) + 0.001).toFixed(3);
        }

        div.innerHTML = legendTitle + div.innerHTML;
        div.innerHTML += lines;
        return div;
    };
    legend.addTo(map);

  }

  function initHeatmap() {

     heat = L.heatLayer(heatmapPoints, {
        minOpacity : 0.7,
        max : 0.9,
        radius : 18,
        blur : 23,
        gradient : {0.22: 'blue', 0.72: 'salmon', 1: 'dimgray'}
    });

  }

  function initMarkers() {

    var popupTemplate = "
{City}

" +
                        "Population: {Population}
" +
                        "GitHub accounts: {Total}
" +
                        "% with account \n" +
                        ": {Rate}

";

    L.Util.ajax(ghGeojsonUrl).then(function(data){

      ghLayer = new L.geoJson(data, {
          pointToLayer: function (geojson, latlng) {

              var rate = geojson.properties.Rate;
              var icon;
              if (rate > maxRate) maxRate = rate;
              icon = icons[1];

              if (rate >= 1.344 && rate <= 1.679 ) icon = icons[4];
              if (rate >= 1.008 && rate <= 1.343 ) icon = icons[3];
              if (rate >= 0.673 && rate <= 1.007 ) icon = icons[2];
              if (rate >= 0.337 && rate <= 0.672 ) icon = icons[1];
              if (rate >= 0.000 && rate <= 0.336 ) icon = icons[0];

              return L.marker(latlng, {
                  icon: L.icon({
                      iconUrl: icon[2],
                      iconSize: [icon[0], icon[1]],
                      iconAnchor: [icon[0] / 2, icon[1] / 2],
                      popupAnchor: [0, -11]
                  })
              })

              // Grow and shrink on mouse over, mouse out
              .on("mouseover",function(marker){
                var icon = marker.target._icon;
                icon.style.width = icon.clientWidth * 1.2 + "px";
                icon.style.height = icon.clientHeight * 1.2 + "px";
              })
              .on("mouseout",function(marker){
                var icon = marker.target._icon;
                icon.style.width = icon.clientWidth * 0.8 + "px";
                icon.style.height = icon.clientHeight * 0.8 + "px";
              });

          },

          onEachFeature : function(feature, layer) {
            var point = [feature.geometry.coordinates[1], feature.geometry.coordinates[0]];
            point.push(feature.properties.Rate);
            heatmapPoints.push(point);

            feature.properties.Rate = parseFloat(feature.properties.Rate).toFixed(3);
            layer.bindPopup(L.Util.template(popupTemplate, feature.properties));
          }

      }).addTo(map);

      document.getElementById("loading").outerHTML = "";
      initHeatmap();
      initLegend();

    });

  }

  function toggleMarkers(opacity) {
    var markers = document.getElementsByClassName("leaflet-marker-icon");
    for(var i = 0; i < markers.length; i++){
      markers.item(i).style.opacity = opacity;
    }
  }

  function toggleRenderer(event) {

    event.stopPropagation();

    if (viewMode === "markers") {
      toggleMarkers(0);
      heat.addTo(map);
      document.getElementsByClassName("legend")[0].style.opacity = 0;
      document.getElementById("switch").innerHTML = "Markers";
      viewMode = "heatmap";
    } else {
      toggleMarkers(1);
      map.removeLayer(heat);
      viewMode = "markers";
      setMarkerOpacity();
      document.getElementsByClassName("legend")[0].style.opacity = 1;
      document.getElementById("switch").innerHTML = "Heatmap";
    }

  }

  function setMarkerOpacity() {

    var markerOpacities = {
      5 : 0.05,
      4 : 0.10,
      3 : 0.18,
      2 : 0.20,
      1 : 0.30
    };

    if (viewMode === "heatmap") return;
    var zoom = map.getZoom();
    var scaler = 2.1;
    var min = 0.22;

    for (var id in ghLayer._layers) {
      var marker = ghLayer._layers[id];
      var rate = parseFloat(marker.feature.properties.Rate); // Let's give every place some opacity at least
      var scaledOpacity = (((rate / maxRate) * scaler) - markerOpacities[zoom]) + min;
      var opacity = map.getZoom() < 6 ? scaledOpacity : 1;
      marker._icon.style.opacity = opacity;
    }

  }

})();
<commit_msg>Over large amounts<commit_after>(function() {

  var map;
  var ghLayer;
  var heat;
  var bands = 5;
  var maxRate = 0;
  var viewMode = "markers";
  var ghGeojsonUrl = "data/github-cities.geojson";

  var heatmapPoints = [];

  var icons = [
      [25, 23, "imgs/github.png"],
      [35, 33, "imgs/redpolo.png"],
      [45, 43, "imgs/americanfootball.png"],
      [55, 53, "imgs/jetpack.png"],
      [65, 63, "imgs/professor.png"]
  ];

  init();

  function init() {

    var center = [ 54.514, -2.122];
    var zoom = 6;
    map = L.map('map').setView(center, zoom);
    L.esri.basemapLayer("Gray").addTo(map);
    L.esri.basemapLayer("GrayLabels").addTo(map);

    initMarkers();
    initListners();

  }

  function initListners() {

    map.on('zoomend', setMarkerOpacity);
    document.getElementById("switch").addEventListener("click", toggleRenderer);

  }

  function initLegend() {

    var legend = L.control({position: 'bottomleft'});
    legend.onAdd = function (map) {

        var div = L.DomUtil.create('div', 'legend');
        var legendTitle = ' % of Pop. with GitHub';

        //Loop through our density intervals and generate a label with a colored square for each interval
        var lowerBounds = "0.000";
        var upperBounds;
        var lines = "";

        for (var i = 0; i < bands; i++) {
            upperBounds = ((maxRate / bands) * (i + 1)).toFixed(3);
            bounds = lowerBounds+' - '+upperBounds;
            lines = '
'+bounds+'' +
                    '
' + lines;
            lowerBounds = (parseFloat(upperBounds) + 0.001).toFixed(3);
        }

        div.innerHTML = legendTitle + div.innerHTML;
        div.innerHTML += lines;
        return div;
    };
    legend.addTo(map);

  }

  function initHeatmap() {

     heat = L.heatLayer(heatmapPoints, {
        minOpacity : 0.7,
        max : 0.9,
        radius : 18,
        blur : 23,
        gradient : {0.22: 'blue', 0.72: 'salmon', 1: 'dimgray'}
    });

  }

  function initMarkers() {

    var popupTemplate = "
{City}

" +
                        "Population: {Population}
" +
                        "GitHub accounts: {Total}
" +
                        "% with account \n" +
                        ": {Rate}

";

    L.Util.ajax(ghGeojsonUrl).then(function(data){

      ghLayer = new L.geoJson(data, {
          pointToLayer: function (geojson, latlng) {

              var rate = geojson.properties.Rate;
              var icon;
              if (rate > maxRate) maxRate = rate;
              icon = icons[1];

              if (rate >= 1.344) icon = icons[4];
              if (rate >= 1.008 && rate <= 1.343 ) icon = icons[3];
              if (rate >= 0.673 && rate <= 1.007 ) icon = icons[2];
              if (rate >= 0.337 && rate <= 0.672 ) icon = icons[1];
              if (rate >= 0.000 && rate <= 0.336 ) icon = icons[0];

              return L.marker(latlng, {
                  icon: L.icon({
                      iconUrl: icon[2],
                      iconSize: [icon[0], icon[1]],
                      iconAnchor: [icon[0] / 2, icon[1] / 2],
                      popupAnchor: [0, -11]
                  })
              })

              // Grow and shrink on mouse over, mouse out
              .on("mouseover",function(marker){
                var icon = marker.target._icon;
                icon.style.width = icon.clientWidth * 1.2 + "px";
                icon.style.height = icon.clientHeight * 1.2 + "px";
              })
              .on("mouseout",function(marker){
                var icon = marker.target._icon;
                icon.style.width = icon.clientWidth * 0.8 + "px";
                icon.style.height = icon.clientHeight * 0.8 + "px";
              });

          },

          onEachFeature : function(feature, layer) {
            var point = [feature.geometry.coordinates[1], feature.geometry.coordinates[0]];
            point.push(feature.properties.Rate);
            heatmapPoints.push(point);

            feature.properties.Rate = parseFloat(feature.properties.Rate).toFixed(3);
            layer.bindPopup(L.Util.template(popupTemplate, feature.properties));
          }

      }).addTo(map);

      document.getElementById("loading").outerHTML = "";
      initHeatmap();
      initLegend();

    });

  }

  function toggleMarkers(opacity) {
    var markers = document.getElementsByClassName("leaflet-marker-icon");
    for(var i = 0; i < markers.length; i++){
      markers.item(i).style.opacity = opacity;
    }
  }

  function toggleRenderer(event) {

    event.stopPropagation();

    if (viewMode === "markers") {
      toggleMarkers(0);
      heat.addTo(map);
      document.getElementsByClassName("legend")[0].style.opacity = 0;
      document.getElementById("switch").innerHTML = "Markers";
      viewMode = "heatmap";
    } else {
      toggleMarkers(1);
      map.removeLayer(heat);
      viewMode = "markers";
      setMarkerOpacity();
      document.getElementsByClassName("legend")[0].style.opacity = 1;
      document.getElementById("switch").innerHTML = "Heatmap";
    }

  }

  function setMarkerOpacity() {

    var markerOpacities = {
      5 : 0.05,
      4 : 0.10,
      3 : 0.18,
      2 : 0.20,
      1 : 0.30
    };

    if (viewMode === "heatmap") return;
    var zoom = map.getZoom();
    var scaler = 2.1;
    var min = 0.22;

    for (var id in ghLayer._layers) {
      var marker = ghLayer._layers[id];
      var rate = parseFloat(marker.feature.properties.Rate); // Let's give every place some opacity at least
      var scaledOpacity = (((rate / maxRate) * scaler) - markerOpacities[zoom]) + min;
      var opacity = map.getZoom() < 6 ? scaledOpacity : 1;
      marker._icon.style.opacity = opacity;
    }

  }

})();
<|endoftext|>


<commit_before>var Google_Loader = (function (window, document) {

    that = this;

	Google_Loader = function (opts) {

        this.int_loading_state = 0;

        this.current_loop = 0;

    	this.fn_loading_screen_animation();

		window.addEventListener('load', function() {
		    FastClick.attach(document.body);
		}, false);

		$('.replay-message-button').on('click', this.fn_replay.bind(this));

	};

	Google_Loader.prototype = {

		// ------------------------------------------------------
		// Skimmin Core Functions
		// ------------------------------------------------------

        fn_replay: function() {

        	$('.replay-message-button').removeClass('animated bounceIn');
        	$('.replay-message-button').addClass('animated rotateOut');

        	setTimeout(function() {
        		$('.replay-message-button').fadeOut(0);
        		$('.replay-message-button').removeClass('animated rotateOut');
        	}, 500);

        	loading_circles = '
' +
					'

' +
					'

' +
				'
' +
				'
' +
					'

' +
					'

' +
				'
' +
				'
' +
					'

' +
					'

' +
				'
' +
				'
' +
					'

' +
					'

' +
				'
';

        	$('#loading-container').fadeIn(0);
        	$('#loading-circles-container').fadeIn(0);
        	$('#loading-circles-container').html('');
        	$('#loading-circles-container').html(loading_circles);

        	this.fn_loading_screen_animation();

        },

        // one full "loop"  -- this function is called in another, high level loading function
        fn_loading_screen_animation: function(int_degrees) {

            if (this.current_loop == 0) {

            	setTimeout(function() {
            		$('#loading-inner-circle-1').fadeIn(0);
            		$('#loading-inner-circle-1').addClass('animated bounceIn');
            	}, 1000);

            	setTimeout(function() {
            		$('#loading-inner-circle-2').fadeIn(0);
            		$('#loading-inner-circle-2').addClass('animated bounceIn');
            	}, 1500);

            	setTimeout(function() {
            		$('#loading-inner-circle-3').fadeIn(0);
            		$('#loading-inner-circle-3').addClass('animated bounceIn');
            	}, 2000);

                setTimeout(function() {
                	$('#loading-inner-circle-4').fadeIn(0);
            		$('#loading-inner-circle-4').addClass('animated bounceIn');
            	}, 2500);

            }

            else {

            	$('#loading-circle-1').fadeIn(0);
            	$('#loading-circle-1').addClass('animated zoomIn');

            	setTimeout(function() {
            		$('#loading-circle-2').fadeIn(0);
            		$('#loading-circle-2').addClass('animated zoomIn');
            	}, 75);

            	setTimeout(function() {
            		$('#loading-circle-3').fadeIn(0);
            		$('#loading-circle-3').addClass('animated zoomIn');
            	}, 150);

            	setTimeout(function() {
            		$('#loading-circle-4').fadeIn(0);
            		$('#loading-circle-4').addClass('animated zoomIn');
            	}, 225);

            	setTimeout(function() {
            		$('#loading-circle-1').removeClass('animated zoomIn');
            		$('#loading-circle-2').removeClass('animated zoomIn');
            		$('#loading-circle-3').removeClass('animated zoomIn');
            		$('#loading-circle-4').removeClass('animated zoomIn');
            	}, 1000);

            	setTimeout(function() {
        		$('#loading-inner-circle-1').fadeIn(0);
        		$('#loading-inner-circle-1').addClass('animated bounceIn');
        	}, 1225);

           	setTimeout(function() {
        		$('#loading-inner-circle-2').fadeIn(0);
       			$('#loading-inner-circle-2').addClass('animated bounceIn');
        	}, 1725);

            	setTimeout(function() {
        		$('#loading-inner-circle-3').fadeIn(0);
        		$('#loading-inner-circle-3').addClass('animated bounceIn');
        	}, 2225);

            	setTimeout(function() {
        		$('#loading-inner-circle-4').fadeIn(0);
        		$('#loading-inner-circle-4').addClass('animated bounceIn');
        	}, 2725);

            }

            // spin the colored circles, while removing the small white ones
            setTimeout(function() {
            	$('#loading-inner-circle-1').removeClass('animated bounceIn');
                $('#loading-circle-1').css('-webkit-transform', 'rotateY(-' + '180' + 'deg)');
               	setTimeout(function() {
                	$('#loading-inner-circle-1').fadeOut(30);
                	$('#loading-inner-circle-1').removeClass('animated bounceIn')
            	}, 260);
            }, 3900);

            setTimeout(function() {
            	$('#loading-inner-circle-2').removeClass('animated bounceIn');
               	$('#loading-circle-2').css('-webkit-transform', 'rotateY(-' + '180' + 'deg)');
               	setTimeout(function() {
               		$('#loading-inner-circle-2').fadeOut(30);
            	}, 260);
            }, 4400);

            setTimeout(function() {
            	$('#loading-inner-circle-3').removeClass('animated bounceIn');
                $('#loading-circle-3').css('-webkit-transform', 'rotateY(-' + '180' + 'deg)');
               	setTimeout(function() {
               		$('#loading-inner-circle-3').fadeOut(30);
            	}, 260);
            }, 4900);

            setTimeout(function() {
            	$('#loading-inner-circle-4').removeClass('animated bounceIn');
               	$('#loading-circle-4').css('-webkit-transform', 'rotateY(-' + '180' + 'deg)');
               	setTimeout(function() {
                	$('#loading-inner-circle-4').fadeOut(30);
            	}, 260);
            }, 5400);

            setTimeout(function() {
       		$('#loading-inner-circle-1').fadeIn(0);
       		$('#loading-inner-circle-1').addClass('animated bounceIn');
            }, 6400);

            setTimeout(function() {
       		$('#loading-inner-circle-2').fadeIn(0);
       		$('#loading-inner-circle-2').addClass('animated bounceIn');
            }, 6900);

            setTimeout(function() {
       		$('#loading-inner-circle-3').fadeIn(0);
       		$('#loading-inner-circle-3').addClass('animated bounceIn');
            }, 7400);

            setTimeout(function() {
       		$('#loading-inner-circle-4').fadeIn(0);
       		$('#loading-inner-circle-4').addClass('animated bounceIn');
            }, 7900);

            setTimeout(function() {
           	$('#loading-circle-1').addClass('animated bounceOut');
            	$('#loading-circle-2').addClass('animated bounceOut');
            	$('#loading-circle-3').addClass('animated bounceOut');
            	$('#loading-circle-4').addClass('animated bounceOut');
            }, 8400);

            setTimeout(function() {
        	$('.replay-message-button').fadeIn(0);
        	$('.replay-message-button').addClass('animated bounceIn');
       		$('#loading-container').fadeOut(500);
            }, 8925);

            /*setTimeout(function() {
        		$('#loading-inner-circle-1').fadeOut(0);
    			$('#loading-inner-circle-2').fadeOut(0);
    			$('#loading-inner-circle-3').fadeOut(0);
        		$('#loading-inner-circle-4').fadeOut(0);
            		$('#loading-circles-container').fadeOut(0);

        		$('#loading-inner-circle-1').fadeOut(0);
        		$('#loading-inner-circle-1').removeClass('animated bounceIn');
        		$('#loading-inner-circle-2').fadeOut(0);
        		$('#loading-inner-circle-2').removeClass('animated bounceIn');
        		$('#loading-inner-circle-3').fadeOut(0);
        		$('#loading-inner-circle-3').removeClass('animated bounceIn');
        		$('#loading-inner-circle-4').fadeOut(0);
        		$('#loading-inner-circle-4').removeClass('animated bounceIn');

        	}, 5125);*/

            this.current_loop += 1;

        },

	};

	return Google_Loader;

})(window, document);
<commit_msg>Update google-loader.js<commit_after>var Google_Loader = (function (window, document) {

	that = this;

	Google_Loader = function (opts) {

        	this.int_loading_state = 0;

        	this.current_loop = 0;

        	window.addEventListener('load', function() {
			FastClick.attach(document.body);
		}, false);

    		this.fn_loading_screen_animation();

		$('.replay-message-button').on('click', this.fn_replay.bind(this));

	};

	Google_Loader.prototype = {

		// ------------------------------------------------------
		// Skimmin Core Functions
		// ------------------------------------------------------

        	fn_replay: function() {

        		$('.replay-message-button').removeClass('animated bounceIn');
        		$('.replay-message-button').addClass('animated rotateOut');

        		setTimeout(function() {
        			$('.replay-message-button').fadeOut(0);
        			$('.replay-message-button').removeClass('animated rotateOut');
        		}, 500);

        		loading_circles = '
' +
					'

' +
					'

' +
				'
' +
				'
' +
					'

' +
					'

' +
				'
' +
				'
' +
					'

' +
					'

' +
				'
' +
				'
' +
					'

' +
					'

' +
				'
';

        		$('#loading-container').fadeIn(0);
        		$('#loading-circles-container').fadeIn(0);
        		$('#loading-circles-container').html('');
        		$('#loading-circles-container').html(loading_circles);

        		this.fn_loading_screen_animation();

        	},

        	// one full "loop"  -- this function is called in another, high level loading function
        	fn_loading_screen_animation: function(int_degrees) {

        		if (this.current_loop == 0) {

            			setTimeout(function() {
            				$('#loading-inner-circle-1').fadeIn(0);
            				$('#loading-inner-circle-1').addClass('animated bounceIn');
            			}, 1000);

            			setTimeout(function() {
            				$('#loading-inner-circle-2').fadeIn(0);
            				$('#loading-inner-circle-2').addClass('animated bounceIn');
            			}, 1500);

            			setTimeout(function() {
            				$('#loading-inner-circle-3').fadeIn(0);
            				$('#loading-inner-circle-3').addClass('animated bounceIn');
            			}, 2000);

       				setTimeout(function() {
                			$('#loading-inner-circle-4').fadeIn(0);
            				$('#loading-inner-circle-4').addClass('animated bounceIn');
            			}, 2500);

            		}

            		else {

            			$('#loading-circle-1').fadeIn(0);
            			$('#loading-circle-1').addClass('animated zoomIn');

            			setTimeout(function() {
            				$('#loading-circle-2').fadeIn(0);
            				$('#loading-circle-2').addClass('animated zoomIn');
            			}, 75);

            			setTimeout(function() {
            				$('#loading-circle-3').fadeIn(0);
            				$('#loading-circle-3').addClass('animated zoomIn');
            			}, 150);

            			setTimeout(function() {
            				$('#loading-circle-4').fadeIn(0);
            				$('#loading-circle-4').addClass('animated zoomIn');
            			}, 225);

            			setTimeout(function() {
            				$('#loading-circle-1').removeClass('animated zoomIn');
            				$('#loading-circle-2').removeClass('animated zoomIn');
            				$('#loading-circle-3').removeClass('animated zoomIn');
            				$('#loading-circle-4').removeClass('animated zoomIn');
            			}, 1000);

            			setTimeout(function() {
        				$('#loading-inner-circle-1').fadeIn(0);
        				$('#loading-inner-circle-1').addClass('animated bounceIn');
        			}, 1225);

           			setTimeout(function() {
        				$('#loading-inner-circle-2').fadeIn(0);
       					$('#loading-inner-circle-2').addClass('animated bounceIn');
        			}, 1725);

            			setTimeout(function() {
        				$('#loading-inner-circle-3').fadeIn(0);
        				$('#loading-inner-circle-3').addClass('animated bounceIn');
        			}, 2225);

            			setTimeout(function() {
        				$('#loading-inner-circle-4').fadeIn(0);
        				$('#loading-inner-circle-4').addClass('animated bounceIn');
        			}, 2725);

            		}

            		// spin the colored circles, while removing the small white ones
            		setTimeout(function() {
            			$('#loading-inner-circle-1').removeClass('animated bounceIn');
                		$('#loading-circle-1').css('-webkit-transform', 'rotateY(-' + '180' + 'deg)');
               			setTimeout(function() {
                			$('#loading-inner-circle-1').fadeOut(30);
                			$('#loading-inner-circle-1').removeClass('animated bounceIn')
            			}, 260);
            		}, 3900);

            		setTimeout(function() {
            			$('#loading-inner-circle-2').removeClass('animated bounceIn');
               			$('#loading-circle-2').css('-webkit-transform', 'rotateY(-' + '180' + 'deg)');
               			setTimeout(function() {
               				$('#loading-inner-circle-2').fadeOut(30);
            			}, 260);
        	 	}, 4400);

            		setTimeout(function() {
            			$('#loading-inner-circle-3').removeClass('animated bounceIn');
                		$('#loading-circle-3').css('-webkit-transform', 'rotateY(-' + '180' + 'deg)');
               			setTimeout(function() {
               				$('#loading-inner-circle-3').fadeOut(30);
            			}, 260);
            		}, 4900);

            		setTimeout(function() {
            			$('#loading-inner-circle-4').removeClass('animated bounceIn');
               			$('#loading-circle-4').css('-webkit-transform', 'rotateY(-' + '180' + 'deg)');
               			setTimeout(function() {
                			$('#loading-inner-circle-4').fadeOut(30);
            			}, 260);
            		}, 5400);

            		setTimeout(function() {
       				$('#loading-inner-circle-1').fadeIn(0);
       				$('#loading-inner-circle-1').addClass('animated bounceIn');
            		}, 6400);

        	 	setTimeout(function() {
       				$('#loading-inner-circle-2').fadeIn(0);
       				$('#loading-inner-circle-2').addClass('animated bounceIn');
            		}, 6900);

            		setTimeout(function() {
       				$('#loading-inner-circle-3').fadeIn(0);
       				$('#loading-inner-circle-3').addClass('animated bounceIn');
            		}, 7400);

        		setTimeout(function() {
       				$('#loading-inner-circle-4').fadeIn(0);
       				$('#loading-inner-circle-4').addClass('animated bounceIn');
            		}, 7900);

            		setTimeout(function() {
           			$('#loading-circle-1').addClass('animated bounceOut');
            			$('#loading-circle-2').addClass('animated bounceOut');
            			$('#loading-circle-3').addClass('animated bounceOut');
            			$('#loading-circle-4').addClass('animated bounceOut');
            		}, 8400);

            		setTimeout(function() {
        			$('.replay-message-button').fadeIn(0);
        			$('.replay-message-button').addClass('animated bounceIn');
       				$('#loading-container').fadeOut(500);
            		}, 8925);

            		this.current_loop += 1;

        	},

	};

	return Google_Loader;

})(window, document);
<|endoftext|>


<commit_before>/**
 * Module requirements.
 */

var XMLHttpRequest = require('xmlhttprequest-ssl');
var Polling = require('./polling');
var Emitter = require('component-emitter');
var inherit = require('component-inherit');
var debug = require('debug')('engine.io-client:polling-xhr');

/**
 * Module exports.
 */

module.exports = XHR;
module.exports.Request = Request;

/**
 * Empty function
 */

function empty(){}

/**
 * XHR Polling constructor.
 *
 * @param {Object} opts
 * @api public
 */

function XHR(opts){
  Polling.call(this, opts);

  if (global.location) {
    var isSSL = 'https:' == location.protocol;
    var port = location.port;

    // some user agents have empty `location.port`
    if (!port) {
      port = isSSL ? 443 : 80;
    }

    this.xd = opts.hostname != global.location.hostname ||
      port != opts.port;
    this.xs = opts.secure != isSSL;
  } else {
    this.extraHeaders = opts.extraHeaders;
  }
}

/**
 * Inherits from Polling.
 */

inherit(XHR, Polling);

/**
 * XHR supports binary
 */

XHR.prototype.supportsBinary = true;

/**
 * Creates a request.
 *
 * @param {String} method
 * @api private
 */

XHR.prototype.request = function(opts){
  opts = opts || {};
  opts.uri = this.uri();
  opts.xd = this.xd;
  opts.xs = this.xs;
  opts.agent = this.agent || false;
  opts.supportsBinary = this.supportsBinary;
  opts.enablesXDR = this.enablesXDR;

  // SSL options for Node.js client
  opts.pfx = this.pfx;
  opts.key = this.key;
  opts.passphrase = this.passphrase;
  opts.cert = this.cert;
  opts.ca = this.ca;
  opts.ciphers = this.ciphers;
  opts.rejectUnauthorized = this.rejectUnauthorized;

  // other options for Node.js client
  opts.extraHeaders = this.extraHeaders;

  return new Request(opts);
};

/**
 * Sends data.
 *
 * @param {String} data to send.
 * @param {Function} called upon flush.
 * @api private
 */

XHR.prototype.doWrite = function(data, fn){
  var isBinary = typeof data !== 'string' && data !== undefined;
  var req = this.request({ method: 'POST', data: data, isBinary: isBinary });
  var self = this;
  req.on('success', fn);
  req.on('error', function(err){
    self.onError('xhr post error', err);
  });
  this.sendXhr = req;
};

/**
 * Starts a poll cycle.
 *
 * @api private
 */

XHR.prototype.doPoll = function(){
  debug('xhr poll');
  var req = this.request();
  var self = this;
  req.on('data', function(data){
    self.onData(data);
  });
  req.on('error', function(err){
    self.onError('xhr poll error', err);
  });
  this.pollXhr = req;
};

/**
 * Request constructor
 *
 * @param {Object} options
 * @api public
 */

function Request(opts){
  this.method = opts.method || 'GET';
  this.uri = opts.uri;
  this.xd = !!opts.xd;
  this.xs = !!opts.xs;
  this.async = false !== opts.async;
  this.data = undefined != opts.data ? opts.data : null;
  this.agent = opts.agent;
  this.isBinary = opts.isBinary;
  this.supportsBinary = opts.supportsBinary;
  this.enablesXDR = opts.enablesXDR;

  // SSL options for Node.js client
  this.pfx = opts.pfx;
  this.key = opts.key;
  this.passphrase = opts.passphrase;
  this.cert = opts.cert;
  this.ca = opts.ca;
  this.ciphers = opts.ciphers;
  this.rejectUnauthorized = opts.rejectUnauthorized;

  // other options for Node.js client
  this.extraHeaders = opts.extraHeaders;

  this.create();
}

/**
 * Mix in `Emitter`.
 */

Emitter(Request.prototype);

/**
 * Creates the XHR object and sends the request.
 *
 * @api private
 */

Request.prototype.create = function(){
  var opts = { agent: this.agent, xdomain: this.xd, xscheme: this.xs, enablesXDR: this.enablesXDR };

  // SSL options for Node.js client
  opts.pfx = this.pfx;
  opts.key = this.key;
  opts.passphrase = this.passphrase;
  opts.cert = this.cert;
  opts.ca = this.ca;
  opts.ciphers = this.ciphers;
  opts.rejectUnauthorized = this.rejectUnauthorized;

  var xhr = this.xhr = new XMLHttpRequest(opts);
  var self = this;

  try {
    debug('xhr open %s: %s', this.method, this.uri);
    xhr.open(this.method, this.uri, this.async);
    try {
      if (this.extraHeaders) {
        xhr.setDisableHeaderCheck(true);
        for (var i in this.extraHeaders) {
          if (this.extraHeaders.hasOwnProperty(i)) {
            xhr.setRequestHeader(i, this.extraHeaders[i]);
          }
        }
      }
    } catch (e) {}
    if (this.supportsBinary) {
      // This has to be done after open because Firefox is stupid
      // http://stackoverflow.com/questions/13216903/get-binary-data-with-xmlhttprequest-in-a-firefox-extension
      xhr.responseType = 'arraybuffer';
    }

    if ('POST' == this.method) {
      try {
        if (this.isBinary) {
          xhr.setRequestHeader('Content-type', 'application/octet-stream');
        } else {
          xhr.setRequestHeader('Content-type', 'text/plain;charset=UTF-8');
        }
      } catch (e) {}
    }

    // ie6 check
    if ('withCredentials' in xhr) {
      xhr.withCredentials = true;
    }

    if (this.hasXDR()) {
      xhr.onload = function(){
        self.onLoad();
      };
      xhr.onerror = function(){
        self.onError(xhr.responseText);
      };
    } else {
      xhr.onreadystatechange = function(){
        if (4 != xhr.readyState) return;
        if (200 == xhr.status || 1223 == xhr.status) {
          self.onLoad();
        } else {
          // make sure the `error` event handler that's user-set
          // does not throw in the same tick and gets caught here
          setTimeout(function(){
            self.onError(xhr.status);
          }, 0);
        }
      };
    }

    debug('xhr data %s', this.data);
    xhr.send(this.data);
  } catch (e) {
    // Need to defer since .create() is called directly fhrom the constructor
    // and thus the 'error' event can only be only bound *after* this exception
    // occurs.  Therefore, also, we cannot throw here at all.
    setTimeout(function() {
      self.onError(e);
    }, 0);
    return;
  }

  if (global.document) {
    this.index = Request.requestsCount++;
    Request.requests[this.index] = this;
  }
};

/**
 * Called upon successful response.
 *
 * @api private
 */

Request.prototype.onSuccess = function(){
  this.emit('success');
  this.cleanup();
};

/**
 * Called if we have data.
 *
 * @api private
 */

Request.prototype.onData = function(data){
  this.emit('data', data);
  this.onSuccess();
};

/**
 * Called upon error.
 *
 * @api private
 */

Request.prototype.onError = function(err){
  this.emit('error', err);
  this.cleanup(true);
};

/**
 * Cleans up house.
 *
 * @api private
 */

Request.prototype.cleanup = function(fromError){
  if ('undefined' == typeof this.xhr || null === this.xhr) {
    return;
  }
  // xmlhttprequest
  if (this.hasXDR()) {
    this.xhr.onload = this.xhr.onerror = empty;
  } else {
    this.xhr.onreadystatechange = empty;
  }

  if (fromError) {
    try {
      this.xhr.abort();
    } catch(e) {}
  }

  if (global.document) {
    delete Request.requests[this.index];
  }

  this.xhr = null;
};

/**
 * Called upon load.
 *
 * @api private
 */

Request.prototype.onLoad = function(){
  var data;
  try {
    var contentType;
    try {
      contentType = this.xhr.getResponseHeader('Content-Type').split(';')[0];
    } catch (e) {}
    if (contentType === 'application/octet-stream') {
      data = this.xhr.response;
    } else {
      if (!this.supportsBinary) {
        data = this.xhr.responseText;
      } else {
        try {
          data = String.fromCharCode.apply(null, new Uint8Array(this.xhr.response));
        } catch (e) {
          var ui8Arr = new Uint8Array(this.xhr.response);
          var dataArray = [];
          for (var idx = 0, length = ui8Arr.legnth; idx < length; idx++) {
            dataArray.push(ui8Arr[idx]);
          }

          data = String.fromCharCode.apply(null, dataArray);
        }
      }
    }
  } catch (e) {
    this.onError(e);
  }
  if (null != data) {
    this.onData(data);
  }
};

/**
 * Check if it has XDomainRequest.
 *
 * @api private
 */

Request.prototype.hasXDR = function(){
  return 'undefined' !== typeof global.XDomainRequest && !this.xs && this.enablesXDR;
};

/**
 * Aborts the request.
 *
 * @api public
 */

Request.prototype.abort = function(){
  this.cleanup();
};

/**
 * Aborts pending requests when unloading the window. This is needed to prevent
 * memory leaks (e.g. when using IE) and to ensure that no spurious error is
 * emitted.
 */

if (global.document) {
  Request.requestsCount = 0;
  Request.requests = {};
  if (global.attachEvent) {
    global.attachEvent('onunload', unloadHandler);
  } else if (global.addEventListener) {
    global.addEventListener('beforeunload', unloadHandler, false);
  }
}

function unloadHandler() {
  for (var i in Request.requests) {
    if (Request.requests.hasOwnProperty(i)) {
      Request.requests[i].abort();
    }
  }
}
<commit_msg>Fix typo<commit_after>/**
 * Module requirements.
 */

var XMLHttpRequest = require('xmlhttprequest-ssl');
var Polling = require('./polling');
var Emitter = require('component-emitter');
var inherit = require('component-inherit');
var debug = require('debug')('engine.io-client:polling-xhr');

/**
 * Module exports.
 */

module.exports = XHR;
module.exports.Request = Request;

/**
 * Empty function
 */

function empty(){}

/**
 * XHR Polling constructor.
 *
 * @param {Object} opts
 * @api public
 */

function XHR(opts){
  Polling.call(this, opts);

  if (global.location) {
    var isSSL = 'https:' == location.protocol;
    var port = location.port;

    // some user agents have empty `location.port`
    if (!port) {
      port = isSSL ? 443 : 80;
    }

    this.xd = opts.hostname != global.location.hostname ||
      port != opts.port;
    this.xs = opts.secure != isSSL;
  } else {
    this.extraHeaders = opts.extraHeaders;
  }
}

/**
 * Inherits from Polling.
 */

inherit(XHR, Polling);

/**
 * XHR supports binary
 */

XHR.prototype.supportsBinary = true;

/**
 * Creates a request.
 *
 * @param {String} method
 * @api private
 */

XHR.prototype.request = function(opts){
  opts = opts || {};
  opts.uri = this.uri();
  opts.xd = this.xd;
  opts.xs = this.xs;
  opts.agent = this.agent || false;
  opts.supportsBinary = this.supportsBinary;
  opts.enablesXDR = this.enablesXDR;

  // SSL options for Node.js client
  opts.pfx = this.pfx;
  opts.key = this.key;
  opts.passphrase = this.passphrase;
  opts.cert = this.cert;
  opts.ca = this.ca;
  opts.ciphers = this.ciphers;
  opts.rejectUnauthorized = this.rejectUnauthorized;

  // other options for Node.js client
  opts.extraHeaders = this.extraHeaders;

  return new Request(opts);
};

/**
 * Sends data.
 *
 * @param {String} data to send.
 * @param {Function} called upon flush.
 * @api private
 */

XHR.prototype.doWrite = function(data, fn){
  var isBinary = typeof data !== 'string' && data !== undefined;
  var req = this.request({ method: 'POST', data: data, isBinary: isBinary });
  var self = this;
  req.on('success', fn);
  req.on('error', function(err){
    self.onError('xhr post error', err);
  });
  this.sendXhr = req;
};

/**
 * Starts a poll cycle.
 *
 * @api private
 */

XHR.prototype.doPoll = function(){
  debug('xhr poll');
  var req = this.request();
  var self = this;
  req.on('data', function(data){
    self.onData(data);
  });
  req.on('error', function(err){
    self.onError('xhr poll error', err);
  });
  this.pollXhr = req;
};

/**
 * Request constructor
 *
 * @param {Object} options
 * @api public
 */

function Request(opts){
  this.method = opts.method || 'GET';
  this.uri = opts.uri;
  this.xd = !!opts.xd;
  this.xs = !!opts.xs;
  this.async = false !== opts.async;
  this.data = undefined != opts.data ? opts.data : null;
  this.agent = opts.agent;
  this.isBinary = opts.isBinary;
  this.supportsBinary = opts.supportsBinary;
  this.enablesXDR = opts.enablesXDR;

  // SSL options for Node.js client
  this.pfx = opts.pfx;
  this.key = opts.key;
  this.passphrase = opts.passphrase;
  this.cert = opts.cert;
  this.ca = opts.ca;
  this.ciphers = opts.ciphers;
  this.rejectUnauthorized = opts.rejectUnauthorized;

  // other options for Node.js client
  this.extraHeaders = opts.extraHeaders;

  this.create();
}

/**
 * Mix in `Emitter`.
 */

Emitter(Request.prototype);

/**
 * Creates the XHR object and sends the request.
 *
 * @api private
 */

Request.prototype.create = function(){
  var opts = { agent: this.agent, xdomain: this.xd, xscheme: this.xs, enablesXDR: this.enablesXDR };

  // SSL options for Node.js client
  opts.pfx = this.pfx;
  opts.key = this.key;
  opts.passphrase = this.passphrase;
  opts.cert = this.cert;
  opts.ca = this.ca;
  opts.ciphers = this.ciphers;
  opts.rejectUnauthorized = this.rejectUnauthorized;

  var xhr = this.xhr = new XMLHttpRequest(opts);
  var self = this;

  try {
    debug('xhr open %s: %s', this.method, this.uri);
    xhr.open(this.method, this.uri, this.async);
    try {
      if (this.extraHeaders) {
        xhr.setDisableHeaderCheck(true);
        for (var i in this.extraHeaders) {
          if (this.extraHeaders.hasOwnProperty(i)) {
            xhr.setRequestHeader(i, this.extraHeaders[i]);
          }
        }
      }
    } catch (e) {}
    if (this.supportsBinary) {
      // This has to be done after open because Firefox is stupid
      // http://stackoverflow.com/questions/13216903/get-binary-data-with-xmlhttprequest-in-a-firefox-extension
      xhr.responseType = 'arraybuffer';
    }

    if ('POST' == this.method) {
      try {
        if (this.isBinary) {
          xhr.setRequestHeader('Content-type', 'application/octet-stream');
        } else {
          xhr.setRequestHeader('Content-type', 'text/plain;charset=UTF-8');
        }
      } catch (e) {}
    }

    // ie6 check
    if ('withCredentials' in xhr) {
      xhr.withCredentials = true;
    }

    if (this.hasXDR()) {
      xhr.onload = function(){
        self.onLoad();
      };
      xhr.onerror = function(){
        self.onError(xhr.responseText);
      };
    } else {
      xhr.onreadystatechange = function(){
        if (4 != xhr.readyState) return;
        if (200 == xhr.status || 1223 == xhr.status) {
          self.onLoad();
        } else {
          // make sure the `error` event handler that's user-set
          // does not throw in the same tick and gets caught here
          setTimeout(function(){
            self.onError(xhr.status);
          }, 0);
        }
      };
    }

    debug('xhr data %s', this.data);
    xhr.send(this.data);
  } catch (e) {
    // Need to defer since .create() is called directly fhrom the constructor
    // and thus the 'error' event can only be only bound *after* this exception
    // occurs.  Therefore, also, we cannot throw here at all.
    setTimeout(function() {
      self.onError(e);
    }, 0);
    return;
  }

  if (global.document) {
    this.index = Request.requestsCount++;
    Request.requests[this.index] = this;
  }
};

/**
 * Called upon successful response.
 *
 * @api private
 */

Request.prototype.onSuccess = function(){
  this.emit('success');
  this.cleanup();
};

/**
 * Called if we have data.
 *
 * @api private
 */

Request.prototype.onData = function(data){
  this.emit('data', data);
  this.onSuccess();
};

/**
 * Called upon error.
 *
 * @api private
 */

Request.prototype.onError = function(err){
  this.emit('error', err);
  this.cleanup(true);
};

/**
 * Cleans up house.
 *
 * @api private
 */

Request.prototype.cleanup = function(fromError){
  if ('undefined' == typeof this.xhr || null === this.xhr) {
    return;
  }
  // xmlhttprequest
  if (this.hasXDR()) {
    this.xhr.onload = this.xhr.onerror = empty;
  } else {
    this.xhr.onreadystatechange = empty;
  }

  if (fromError) {
    try {
      this.xhr.abort();
    } catch(e) {}
  }

  if (global.document) {
    delete Request.requests[this.index];
  }

  this.xhr = null;
};

/**
 * Called upon load.
 *
 * @api private
 */

Request.prototype.onLoad = function(){
  var data;
  try {
    var contentType;
    try {
      contentType = this.xhr.getResponseHeader('Content-Type').split(';')[0];
    } catch (e) {}
    if (contentType === 'application/octet-stream') {
      data = this.xhr.response;
    } else {
      if (!this.supportsBinary) {
        data = this.xhr.responseText;
      } else {
        try {
          data = String.fromCharCode.apply(null, new Uint8Array(this.xhr.response));
        } catch (e) {
          var ui8Arr = new Uint8Array(this.xhr.response);
          var dataArray = [];
          for (var idx = 0, length = ui8Arr.length; idx < length; idx++) {
            dataArray.push(ui8Arr[idx]);
          }

          data = String.fromCharCode.apply(null, dataArray);
        }
      }
    }
  } catch (e) {
    this.onError(e);
  }
  if (null != data) {
    this.onData(data);
  }
};

/**
 * Check if it has XDomainRequest.
 *
 * @api private
 */

Request.prototype.hasXDR = function(){
  return 'undefined' !== typeof global.XDomainRequest && !this.xs && this.enablesXDR;
};

/**
 * Aborts the request.
 *
 * @api public
 */

Request.prototype.abort = function(){
  this.cleanup();
};

/**
 * Aborts pending requests when unloading the window. This is needed to prevent
 * memory leaks (e.g. when using IE) and to ensure that no spurious error is
 * emitted.
 */

if (global.document) {
  Request.requestsCount = 0;
  Request.requests = {};
  if (global.attachEvent) {
    global.attachEvent('onunload', unloadHandler);
  } else if (global.addEventListener) {
    global.addEventListener('beforeunload', unloadHandler, false);
  }
}

function unloadHandler() {
  for (var i in Request.requests) {
    if (Request.requests.hasOwnProperty(i)) {
      Request.requests[i].abort();
    }
  }
}
<|endoftext|>


<commit_before>{
    "type": "FeatureCollection",
    "features": [

        // Alueet

        {
            "type": "Feature",
            "geometry": {
                "type": "Polygon",
                "coordinates": [
                    [
                        [24.084649085998535, 60.219909127386956],
                        [24.085464477539062, 60.21888599780683],
                        [24.086837768554688, 60.218417052746055],
                        [24.088897705078125, 60.21865152611481],
                        [24.091386795043945, 60.2190352061924],
                        [24.09379005432129, 60.21888599780683],
                        [24.09572124481201, 60.21963203294479],
                        [24.09554958343506, 60.22046330924596],
                        [24.095206260681152, 60.22195529077001],
                        [24.096708297729492, 60.22253075121309],
                        [24.098939895629883, 60.223063575950306],
                        [24.100441932678223, 60.22442756781628],
                        [24.103145599365234, 60.22498167330085],
                        [24.104690551757812, 60.22574888081699],
                        [24.10499095916748, 60.226729175401395],
                        [24.10400390625, 60.22779467974163],
                        [24.100914001464844, 60.22898800348251],
                        [24.09872531890869, 60.22941418000361],
                        [24.095678329467773, 60.22913716589528],
                        [24.093189239501953, 60.22847658434219],
                        [24.091215133666992, 60.22781598947512],
                        [24.089069366455078, 60.22702752010803],
                        [24.08726692199707, 60.22587674699059],
                        [24.08649444580078, 60.22464068641878],
                        [24.086580276489258, 60.223148827104616],
                        [24.08748149871826, 60.22148638960718],
                        [24.085335731506348, 60.22080433958028],
                        [24.084649085998535, 60.219909127386956]
                    ]
                ]
            },
            "properties": {
                "title": "Talmonmäki",
                "description": "Isot kalliot täynnä polkuja."
            }
        },
        {
            "type": "Feature",
            "geometry": {
                "type": "Polygon",
                "coordinates": [
                    [
                        ["24.1973876953125", "60.28820531718946"],
                        ["24.196593761444092", "60.28771609311772"],
                        ["24.196937084197998", "60.28721622617967"],
                        ["24.197001457214355", "60.286748258716194"],
                        ["24.196443557739258", "60.286195197624636"],
                        ["24.19614315032959", "60.285482585888566"],
                        ["24.196550846099854", "60.28465295863219"],
                        ["24.196593761444092", "60.283610576602904"],
                        ["24.196572303771973", "60.282621346603285"],
                        ["24.197280406951904", "60.28222777365918"],
                        ["24.19790267944336", "60.28223841082806"],
                        ["24.19837474822998", "60.28268516879656"],
                        ["24.199018478393555", "60.283217015563636"],
                        ["24.199447631835938", "60.2835148459752"],
                        ["24.199490547180176", "60.284589140279145"],
                        ["24.199190139770508", "60.28536558844776"],
                        ["24.199018478393555", "60.28601438716188"],
                        ["24.199254512786865", "60.28669508017158"],
                        ["24.198803901672363", "60.28724813280828"],
                        ["24.198439121246338", "60.28785435283657"],
                        ["24.1973876953125", "60.28820531718946"]
                    ]
                ]
            },
            "properties": {
                "title": "Kiviahde",
                "description": "Pitkänomainen kallio, jossa menee pohjois-eteläsuunnassa pari polkua päästä päähän. Alueelle pääsee sisään kummastakin päästä. "
            }
        },
        {
            "type": "Feature",
            "geometry": {
                "type": "Polygon",
                "coordinates": [
                    [
                        ["24.106900691986084", "60.27904710499282"],
                        ["24.103896617889404", "60.28064279683376"],
                        ["24.100956916809082", "60.2794832684935"],
                        ["24.100356101989746", "60.2777385795919"],
                        ["24.102094173431396", "60.27764283177559"],
                        ["24.10278081893921", "60.275280963585836"],
                        ["24.103124141693115", "60.27445107752117"],
                        ["24.103682041168213", "60.27602571520698"],
                        ["24.105699062347412", "60.27587676623918"],
                        ["24.107630252838135", "60.277664109092456"],
                        ["24.108102321624756", "60.27779177270223"],
                        ["24.106900691986084", "60.27904710499282"]
                    ]
                ]
            },
            "properties": {
                "title": "Lempolankallio ja kallion takamaastot",
                "description": "Teknistä sekä helpompaa polkua, kalliolta jyrkähkö lasku lammen suuntaan."
            }
        },

        // Polut

        {
            "type": "Feature",
            "geometry": {
                "type": "LineString",
                "coordinates": [
                    [ 102.0, 0.0 ],
                    [ 103.0, 1.0 ],
                    [ 104.0, 0.0 ],
                    [ 105.0, 1.0 ]
                ]
            },
            "properties": {

            }
        },

        // Paikat (pisteet)

        {
            "type": "Feature",
            "geometry": {
                "type": "Point",
                "coordinates": [24.07661212, 60.21987064]
            },
            "properties": {
                "title": "Ketjunkatkaisijaristeys",
                "description": "AM-sedällä oli mennyt totaalisesti hermo tässä polkujen risteyskohdassa ja ilmeisen edullinen ja rimpula Bilteman ketjunkatkaisija oli lentänyt kymmeniä metrejä metsään voimasanojen saattelemana. Pojat käyttivät yhden kesän työkalun etsimiseen ja sen löydyttyä, lahjoitettiin se hienoon terraarioon asennattuna AM:lle pikkujouluissa. Vittuiluhan on kuitenkin välittämistä."
            }
        },

        {
            "type": "Feature",
            "geometry": {
                "type": "Point",
                "coordinates": [24.064393, 60.215518]
            },
            "properties": {
                "title": "G-piste",
                "description": "Gunnarlan maja = G-piste, G-spot, G-Maja jne. Yhdistysten rakennus, jonka laavulla voi paistaa makkuria tms. Aikoinaan 24H -tapahtuman keskiö. Voi vuokrata."
            }
        },

        {
            "type": "Feature",
            "geometry": {
                "type": "Point",
                "coordinates": [24.010695, 60.235679]
            },
            "properties": {
                "title": "Lahokallio",
                "description": "Hienot näkymät Lohjanjärvelle."
            }
        },

        {
            "type": "Feature",
            "geometry": {
                "type": "Point",
                "coordinates": [23.955118, 60.172426]
            },
            "properties": {
                "title": "Mikun uimala",
                "description": "Takuuvarma mutalammikko kuivanakin kesänä."
            }
        }

    ]
}
<commit_msg>Mikun uimala --> Micon uimala<commit_after>{
    "type": "FeatureCollection",
    "features": [

        // Alueet

        {
            "type": "Feature",
            "geometry": {
                "type": "Polygon",
                "coordinates": [
                    [
                        [24.084649085998535, 60.219909127386956],
                        [24.085464477539062, 60.21888599780683],
                        [24.086837768554688, 60.218417052746055],
                        [24.088897705078125, 60.21865152611481],
                        [24.091386795043945, 60.2190352061924],
                        [24.09379005432129, 60.21888599780683],
                        [24.09572124481201, 60.21963203294479],
                        [24.09554958343506, 60.22046330924596],
                        [24.095206260681152, 60.22195529077001],
                        [24.096708297729492, 60.22253075121309],
                        [24.098939895629883, 60.223063575950306],
                        [24.100441932678223, 60.22442756781628],
                        [24.103145599365234, 60.22498167330085],
                        [24.104690551757812, 60.22574888081699],
                        [24.10499095916748, 60.226729175401395],
                        [24.10400390625, 60.22779467974163],
                        [24.100914001464844, 60.22898800348251],
                        [24.09872531890869, 60.22941418000361],
                        [24.095678329467773, 60.22913716589528],
                        [24.093189239501953, 60.22847658434219],
                        [24.091215133666992, 60.22781598947512],
                        [24.089069366455078, 60.22702752010803],
                        [24.08726692199707, 60.22587674699059],
                        [24.08649444580078, 60.22464068641878],
                        [24.086580276489258, 60.223148827104616],
                        [24.08748149871826, 60.22148638960718],
                        [24.085335731506348, 60.22080433958028],
                        [24.084649085998535, 60.219909127386956]
                    ]
                ]
            },
            "properties": {
                "title": "Talmonmäki",
                "description": "Isot kalliot täynnä polkuja."
            }
        },
        {
            "type": "Feature",
            "geometry": {
                "type": "Polygon",
                "coordinates": [
                    [
                        ["24.1973876953125", "60.28820531718946"],
                        ["24.196593761444092", "60.28771609311772"],
                        ["24.196937084197998", "60.28721622617967"],
                        ["24.197001457214355", "60.286748258716194"],
                        ["24.196443557739258", "60.286195197624636"],
                        ["24.19614315032959", "60.285482585888566"],
                        ["24.196550846099854", "60.28465295863219"],
                        ["24.196593761444092", "60.283610576602904"],
                        ["24.196572303771973", "60.282621346603285"],
                        ["24.197280406951904", "60.28222777365918"],
                        ["24.19790267944336", "60.28223841082806"],
                        ["24.19837474822998", "60.28268516879656"],
                        ["24.199018478393555", "60.283217015563636"],
                        ["24.199447631835938", "60.2835148459752"],
                        ["24.199490547180176", "60.284589140279145"],
                        ["24.199190139770508", "60.28536558844776"],
                        ["24.199018478393555", "60.28601438716188"],
                        ["24.199254512786865", "60.28669508017158"],
                        ["24.198803901672363", "60.28724813280828"],
                        ["24.198439121246338", "60.28785435283657"],
                        ["24.1973876953125", "60.28820531718946"]
                    ]
                ]
            },
            "properties": {
                "title": "Kiviahde",
                "description": "Pitkänomainen kallio, jossa menee pohjois-eteläsuunnassa pari polkua päästä päähän. Alueelle pääsee sisään kummastakin päästä. "
            }
        },
        {
            "type": "Feature",
            "geometry": {
                "type": "Polygon",
                "coordinates": [
                    [
                        ["24.106900691986084", "60.27904710499282"],
                        ["24.103896617889404", "60.28064279683376"],
                        ["24.100956916809082", "60.2794832684935"],
                        ["24.100356101989746", "60.2777385795919"],
                        ["24.102094173431396", "60.27764283177559"],
                        ["24.10278081893921", "60.275280963585836"],
                        ["24.103124141693115", "60.27445107752117"],
                        ["24.103682041168213", "60.27602571520698"],
                        ["24.105699062347412", "60.27587676623918"],
                        ["24.107630252838135", "60.277664109092456"],
                        ["24.108102321624756", "60.27779177270223"],
                        ["24.106900691986084", "60.27904710499282"]
                    ]
                ]
            },
            "properties": {
                "title": "Lempolankallio ja kallion takamaastot",
                "description": "Teknistä sekä helpompaa polkua, kalliolta jyrkähkö lasku lammen suuntaan."
            }
        },

        // Polut

        {
            "type": "Feature",
            "geometry": {
                "type": "LineString",
                "coordinates": [
                    [ 102.0, 0.0 ],
                    [ 103.0, 1.0 ],
                    [ 104.0, 0.0 ],
                    [ 105.0, 1.0 ]
                ]
            },
            "properties": {

            }
        },

        // Paikat (pisteet)

        {
            "type": "Feature",
            "geometry": {
                "type": "Point",
                "coordinates": [24.07661212, 60.21987064]
            },
            "properties": {
                "title": "Ketjunkatkaisijaristeys",
                "description": "AM-sedällä oli mennyt totaalisesti hermo tässä polkujen risteyskohdassa ja ilmeisen edullinen ja rimpula Bilteman ketjunkatkaisija oli lentänyt kymmeniä metrejä metsään voimasanojen saattelemana. Pojat käyttivät yhden kesän työkalun etsimiseen ja sen löydyttyä, lahjoitettiin se hienoon terraarioon asennattuna AM:lle pikkujouluissa. Vittuiluhan on kuitenkin välittämistä."
            }
        },

        {
            "type": "Feature",
            "geometry": {
                "type": "Point",
                "coordinates": [24.064393, 60.215518]
            },
            "properties": {
                "title": "G-piste",
                "description": "Gunnarlan maja = G-piste, G-spot, G-Maja jne. Yhdistysten rakennus, jonka laavulla voi paistaa makkuria tms. Aikoinaan 24H -tapahtuman keskiö. Voi vuokrata."
            }
        },

        {
            "type": "Feature",
            "geometry": {
                "type": "Point",
                "coordinates": [24.010695, 60.235679]
            },
            "properties": {
                "title": "Lahokallio",
                "description": "Hienot näkymät Lohjanjärvelle."
            }
        },

        {
            "type": "Feature",
            "geometry": {
                "type": "Point",
                "coordinates": [23.955118, 60.172426]
            },
            "properties": {
                "title": "Micon uimala",
                "description": "Takuuvarma mutalammikko kuivanakin kesänä."
            }
        }

    ]
}
<|endoftext|>