robsite

Google Maps Animated Marker Move

Marker kann man in Google Maps zwar mit setPosition() verschieben, dies wird aber nicht animiert. Da es doch recht schön ist, wenn Marker nicht einfach rumspringen, schrieb ich die folgende Funktion. Einfach einbinden und an einem Marker aufrufen. Optional jQuery und das jQuery Easing Plugin nehmen, um mehr als nur linear zu animieren.

Demo: (auf Karte klicken)

Demo auf eigener Seite

Die Funktion:

// Animated Marker Movement. Robert Gerlach 2012-2013 https://github.com/combatwombat/marker-animate
// MIT license
//
// params:
// newPosition        - the new Position as google.maps.LatLng()
// options            - optional options object (optional)
// options.duration   - animation duration in ms (default 1000)
// options.easing     - easing function from jQuery and/or the jQuery easing plugin (default 'linear')
// options.complete   - callback function. Gets called, after the animation has finished
google.maps.Marker.prototype.animateTo = function(newPosition, options) {
  defaultOptions = {
    duration: 1000,
    easing: 'linear',
    complete: null
  }
  options = options || {};

  // complete missing options
  for (key in defaultOptions) {
    options[key] = options[key] || defaultOptions[key];
  }

  // throw exception if easing function doesn't exist
  if (options.easing != 'linear') {            
    if (typeof jQuery == 'undefined' || !jQuery.easing[options.easing]) {
      throw '"' + options.easing + '" easing function doesn\'t exist. Include jQuery and/or the jQuery easing plugin and use the right function name.';
      return;
    }
  }
  
  window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
  window.cancelAnimationFrame = window.cancelAnimationFrame || window.mozCancelAnimationFrame;

  // save current position. prefixed to avoid name collisions. separate for lat/lng to avoid calling lat()/lng() in every frame
  this.AT_startPosition_lat = this.getPosition().lat();
  this.AT_startPosition_lng = this.getPosition().lng();
  var newPosition_lat = newPosition.lat();
  var newPosition_lng = newPosition.lng();

  // crossing the 180° meridian and going the long way around the earth?
  if (Math.abs(newPosition_lng - this.AT_startPosition_lng) > 180) {
    if (newPosition_lng > this.AT_startPosition_lng) {      
      newPosition_lng -= 360;      
    } else {
      newPosition_lng += 360;
    }
  }

  var animateStep = function(marker, startTime) {            
    var ellapsedTime = (new Date()).getTime() - startTime;
    var durationRatio = ellapsedTime / options.duration; // 0 - 1
    var easingDurationRatio = durationRatio;

    // use jQuery easing if it's not linear
    if (options.easing !== 'linear') {
      easingDurationRatio = jQuery.easing[options.easing](durationRatio, ellapsedTime, 0, 1, options.duration);
    }
    
    if (durationRatio < 1) {
      var deltaPosition = new google.maps.LatLng( marker.AT_startPosition_lat + (newPosition_lat - marker.AT_startPosition_lat)*easingDurationRatio,
                                                  marker.AT_startPosition_lng + (newPosition_lng - marker.AT_startPosition_lng)*easingDurationRatio);
      marker.setPosition(deltaPosition);

      // use requestAnimationFrame if it exists on this browser. If not, use setTimeout with ~60 fps
      if (window.requestAnimationFrame) {
        marker.AT_animationHandler = window.requestAnimationFrame(function() {animateStep(marker, startTime)});                
      } else {
        marker.AT_animationHandler = setTimeout(function() {animateStep(marker, startTime)}, 17); 
      }

    } else {
      
      marker.setPosition(newPosition);

      if (typeof options.complete === 'function') {
        options.complete();
      }

    }            
  }

  // stop possibly running animation
  if (window.cancelAnimationFrame) {
    window.cancelAnimationFrame(this.AT_animationHandler);
  } else {
    clearTimeout(this.AT_animationHandler); 
  }
  
  animateStep(this, (new Date()).getTime());
}

Aufruf:

var marker = new google.maps.Marker({position: new google.maps.LatLng(0,0), map: myMap, title: 'Hello World!'});
var newPosition = new google.maps.LatLng(13,42);

// move marker in 1000ms and with linear animation.
marker.animateTo(newPosition); 

// or with callback and options for easing and duration in milliseconds. Needs jQuery Easing Plugin.
marker.animateTo(newPosition, {  easing: "easeOutBounce",
                                 duration: 1000,
                                 complete: function() {
                                   alert("animation complete");
                                 }
                              });

Das ganze auf github.

· google maps, javascript ·

Moondog

Moondog

Moondog, born Louis Thomas Hardin (May 26, 1916 – September 8, 1999), was a blind American composer, musician, poet and inventor of several musical instruments. Moving to New York as a young man, Moondog made a deliberate decision to make his home on the streets there [...]

Most days he could be found in his chosen part of town wearing clothes he had created based on his own interpretation of the Norse god Odin. Thanks to his unconventional outfits and lifestyle, he was known for much of his life as "The Viking of 6th Avenue".

·

Wette: Anzahl der Videotheken in Deutschland

Ludwig und ich gerieten neulich in ein Argument über Videokonsum in Deutschland, genauer über Videotheken vs. Internet.

Er sagt: Die Anzahl der Videotheken in Deutschland mag in den nächsten 5 Jahren zurückgehen, Video-on-Demand wird aber nicht ausschlaggebend sein. Die Filmrechteinhaber werden nicht schnell genug von der Musikindustrie lernen, legale Videoangebote werden immernoch unzureichend sein (zu teuer, selten Originalsprache, DRM, schlechte Qualität). Nicht genug Leute haben Breitband und einfache Möglichkeiten, Filme online zu leihen/kaufen. Nur furchtlose/dumme Nerds raubkopieren noch. Videotheken sind klasse zum Entdecken neuer Filme.

Ich sage: Legale Video-on-Demand-Angebote sind zwar noch schlecht, Piraterie ist zu kompliziert und gefährlich für die meisten Leute und nicht jeder hat die nötige Breitbandverbindung. Aber trotzdem: Physische Datenträger sind am Aussterben; die Blu-Ray wird das letzte physische Medium für Heimvideos sein. In den nächsten 5 Jahren haben mehr Leute Breitband und die Chance, dass sich die Filmindustrie mit ihren Rechten auskäst, steigt jedes Jahr. Zudem sind Videotheken umständlich. Aus dem Haus gehen um einen Film auszuleihen? Und zurückzubringen? Viel zu aufwändig, Faulheit obsiegt.

Es gibt keine Aussicht auf Einigung zwischen uns, die Fronten sind verhärtet. Zeit, die Einsätze zu erhöhen und eine Wette zu starten:

Bis 2017 wird sich die Anzahl der Videotheken in Deutschland halbieren.
Von 2460 in 2011 auf ≤ 1230 in 2017.

Tritt dieser Fall ein, zahlt Ludwig 100€ an Robert.
Tritt dieser Fall nicht ein, zahlt Robert 100€ an Ludwig.

Quelle der Zahlen sind die Geschäftsberichte des IVD (Interessenverband des Video- und Medienfachhandels in Deutschland e.V.). Der folgende Graph wird fortan jährlich aktualisiert:

Quellen: IVD Geschäftsberichte für 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2014, 2015, 2017

Es wird spannend.

Edit: Sieg! Die Wettschuld ist beglichen, die Wette beendet. Dem Verlauf nach müsste es 2021 die letzte handvoll Videotheken geben. Für die 100€ legen wir dann einen schmucken Trauerkranz vor die letzte.

·

ChunkMail

DailyLit ist klasse. Sendet einem täglich einen kleinen Teil eines langen Buches per Mail oder RSS. E-Mails liest man eh; warum so also nicht auch das Buch, das seit 3 Wochen auf dem Nachttisch verstaubt? Nachteil von DailyLit: Keine eigenen Texte hochladbar.

Drum schrieb ich mir gestern ein simples kleines PHP-Script, das einem auf Anfrage den nächsten Teil eines langen Textes per Mail schickt. Mehr nicht. Keine User-Accounts, multiplen Dateien, Admin-Interfaces etc.
Wer es gebrauchen kann:

<?
/*
ChunkMail
(c) Robert Gerlach 2012 - robsite.de

Sends and displays chunks of a long text per mail. Inspired by DailyLit.com

Usage:

1. Upload chunkmail.php to a PHP capable web server

2. Put a long text file in the same folder as chunkmail.php

3. Edit $config below with the name of your long text file.

4. Make the chunkmail folder writeable by PHP (so chunkmail can create meta_$longTextFile, which stores the already mailed pages)

5. Call chunkmail.php:        
    chunkmail.php           - Display smallest unread page. Mail it, if it hasn't been mailed yet.
    chunkmail.php?page=123  - Display page 123. Mail it, if it hasn't been mailed yet.
    chunkmail.php?reset     - Reset list of mailed pages. Display page 1, mail it.

    To send you the newest page daily at 6:00, use cron:
    > crontab -e
    0 6 * * * wget http://example.com/where/chunkmail/is/chunkmail.php > /dev/null

    
    
Optional: Secure chunkmail folder via .htaccess on Apache:

1. Create a .htpasswd file somewhere your webserver can't access it. You can generate the password with tools like
    - http://www.4webhelp.net/us/password.php
    - http://www.htaccesstools.com/htpasswd-generator/

2. Create .htaccess in the chunkmail folder
    AuthUserFile /absolute/path/to/.htpasswd
    AuthType Basic
    AuthName "ChunkMail"
    Require valid-user    

3. Modify wget in crontab to use HTTP-Authentication:
    wget http://YOUR_USERNAME:YOUR_PASSWORD@example.com/where/chunkmail/is/chunkmail.php > /dev/null


License: WSCECPUCYUS - Wear a Slice of Cheese for Every CPU Core You Use the Software on
*/

$config = array(
    'longTextFile'      => "longtextislong.txt",        // Your long text file
    'chunkSize'         => 3*1024,                      // Chunk size in bytes
    'chunkPadding'      => 512,                         // Chunk padding in bytes. Displays a bit of the previous chunk, so you know where you left off.
    'receiver'          => 'you@somewhere.com'          // Receiver email
);

$serverURL = $_SERVER['HTTP_HOST'].$_SERVER['SCRIPT_NAME'];
if ($_SERVER['HTTPS']) {
    $serverURL = 'https://'.$serverURL;
} else {
    $serverURL = 'http://'.$serverURL;
}

// File with sent page numbers, comma separated
$metaFile = 'meta_'.$config['longTextFile']; 

// Get text length and pages length
$longText = file_get_contents($config['longTextFile']);
$longTextLength = strlen($longText);
$pagesLength = floor($longTextLength / $config['chunkSize']);
if ($pagesLength < 1) {
    $pagesLength = 1;
}

// Reset sent pages. Start from the beginning
$reset = false;
if (isset($_GET['reset'])) {
    file_put_contents($metaFile, "");
    $reset = true;
}

$currentPage = 0;
$sentPages = array(); // all the already sent pages

if (file_exists($metaFile)) {    
    $fileContents = file_get_contents($metaFile);
    
    if (strlen($fileContents) > 0) {
        $sentPagesFromFile = explode(",", $fileContents);
    } 
    
    if (is_array($sentPagesFromFile)) {        
        $sentPages = $sentPagesFromFile;
    }
}

// Get current page (starting with 0). Either the ?page, or the smallest unread page
if (isset($_GET['page'])) {
    $currentPage = $_GET['page']-1;    

} else {
    // Smallest unread page or last page
    $currentPage = $pagesLength - 1;
    for ($i = 0; $i < $pagesLength; $i++) {
        if (!in_array($i."", $sentPages)) {
            $currentPage = $i."";
            break;
        }
    }
}

if ($currentPage >= $pagesLength) {
    $currentPage = $pagesLength - 1;
} else if ($currentPage < 0) {
    $currentPage = 0;
}

// Get text chunk for this page plus a little padding to ease you into the text
$ratio = $currentPage / $pagesLength;
$chunkStart = $ratio * $longTextLength;
$chunkLength = $longTextLength / $pagesLength;
$chunk = substr($longText, $chunkStart, $chunkLength); // don't bother with cutting off words. That's what the padding is for.

$paddingChunkStart = $chunkStart - $config['chunkPadding'];
if ($paddingChunkStart < 0) {
    $paddingChunkStart = 0;
}
$paddingChunkLength = $chunkStart - $paddingChunkStart;
if ($paddingChunkLength > 0) {
    $paddingChunk = substr($longText, $paddingChunkStart, $paddingChunkLength);
} else {
    $paddingChunk = '';
}

$nextPage = $currentPage + 1;
if ($nextPage >= $pagesLength) {
    $nextPage = $pagesLength - 1;
}

$previousPage = $currentPage - 1;
if ($previousPage < 0) {
    $previousPage = 0;
}

// Send current chunk per mail, but only once
$wasSent = false;
if (!in_array($currentPage, $sentPages)) {
    
    $to = $config['receiver'];
    $subject = $config['longTextFile'] . " ".($currentPage+1) . "/" . $pagesLength . " — ChunkMail";
    
    $message = $subject . "\n\n";
    $message .= $paddingChunk."\n\n--------\n\n".$chunk;
    $message .= "\n\n\n";
    $message .= "Send next unread page: " . $serverURL."\n\n";
    $message .= "First Page: " . $serverURL."?page=1\n";
    $message .= "Previous Page: " . $serverURL."?page=".($previousPage + 1)."\n";
    $message .= "Next Page: " . $serverURL."?page=".($nextPage + 1)."\n";
    $message .= "Last Page: " . $serverURL."?page=".($pagesLength)."\n\n";
    $message .= "Reset mailed pages: " . $serverURL."?reset";
    
    $headers =  'From: chunkmail@'.$_SERVER['SERVER_NAME'] . "\r\n" . 
                'Reply-To: chunkmail@'.$_SERVER['SERVER_NAME'] . "\r\n";                    
    
    mail($to, $subject, $message, $headers);    
    
    $sentPages[] = $currentPage;
    $wasSent = true;
}

sort($sentPages); // unnecessary but nice

file_put_contents($metaFile, implode(",", $sentPages));

?><!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
        <title><?= $config['longTextFile']." ". ($currentPage+1) . "/" . $pagesLength?> — ChunkMail</title>

        <style type="text/css" media="screen">
            div {
                margin-bottom: 20px;
            }
            .reset,
            .wasSent {
                font-weight: bold;
            }
            .paddingChunk {
                color: #999;
            }
            .links a {
                display: inline-block;
                padding: 20px 30px;
                text-decoration: none;
            }
            .links a:first-child,
            .links .reset {
                padding-left: 0;
            }
        </style>    
    </head>
    <body>
        
        <? if ($reset) { ?>
          <div class="reset">
            Resetting pages. Now starting with page 1.
          </div>  
        <? } ?>
        
        <? if ($wasSent) { ?>
          <div class="wasSent">
            This page was sent to <?=htmlentities($config['receiver'])?>.
          </div>  
        <? } ?>
        
        <h1>
            <?= $config['longTextFile']." ". ($currentPage+1) . "/" . $pagesLength?>            
        </h1>
        
        <div class="text">
            <span class="paddingChunk"><?=nl2br(htmlentities($paddingChunk))?></span><?=nl2br(htmlentities($chunk))?>
        </div>
        
        <div class="links">
            <a href="<?=$serverURL;?>?page=1" title="First Page">|<</a>
            <a href="<?=$serverURL;?>?page=<?=$previousPage + 1?>" title="Previous Page"><</a>  
            <a href="<?=$serverURL;?>?page=<?=$nextPage + 1?>" title="Next Page">></a>
            <a href="<?=$serverURL;?>?page=<?=$pagesLength?>" title="Last Page">>|</a> 
            <br>
            <a class="reset" href="<?=$serverURL;?>?reset" title="Reset sent pages">Reset</a> 
        </div>
    </body>
</html>

Später sah ich dann, dass Dripread genau das gleiche macht bzw. DailyLit-mit-eigenen-Texten ist :D

·

Waltz — Craig Armstrong — Ein

Als ich den Song hörte, kamen mir die Lyrics bekannt vor.

So genau wie möglich transkribiert:

</nobar>
</iable>=center>=/d=they body / (html = 
<"doctype html public "-// W3C// dtd html 4. transitional // en>
<html>
<head>
<meta http=-eqiv=(Contenttype=content"Text/html;kset=iso-8859-1">

function fensterAufTo(url) {
pict = window.open(url "toolbar=no location=no directories=no status=no men=no ubar=no,
scrollbars=yes resizable=no width 420 hoehe=460 screenX=90 screenY=80
pict.location href=weg(save.location a href)+url=
}
<meta name=generator=content=(EditPlus 1.1)>
<title>news<>
Störung=<base target=blank(_)i
link related in stylesheet type="text/sss" = href="css/agf.css">
<hype/ =
<body style="font family: Arial=" styles

I MISS YOU

<table border=0 cellspacing=0 cellpadding=0>
<b> = small <b nsbp = small 
<this>/small = <p = table/
(body html head script language javascript <"--

HIDE ME

browserOk = false // 001 = 55
script/
(script language) = "javascript 1.1=<"--

HIDE ME

browserOk=true
/--1-(</script/"--

HIDE ME
HIDE ME

bgcolor ffffff
swLiveConnect = false
width = 270
height = 56
= type = application
/

Anscheinend versucht er ein Popup zu öffnen. Musiker und HTML...

· :D, html, musik ·