<commit_before>(function ($) {
  "use strict";
  var arraySize = 10, //size needs to be odd
    bits = 3,
    colorBits = true,
    initialArray,
    array,
    stack,
    mode,
    av = new JSAV($("#jsavcontainer")),
    clickHandler;

  av.recorded(); // we are not recording an AV with an algorithm

  function initialize() {

    exercise.jsav.container.find(".jsavcanvas").css({height: 350});

    //set up click handler
    if (typeof clickHandler === "undefined") {
      clickHandler = new ClickHandler(av, exercise, {effect: "swap", selectedClass: "selected"});
    }
    clickHandler.reset();

    //generate random infix and insert in the array
    initialArray = JSAV.utils.rand.numKeys(0, Math.pow(2, bits), arraySize);
    for (var i = 0; i < arraySize; i++) {
      //convert into binary
      initialArray[i] = initialArray[i].toString(2);
      //add leading zeros
      initialArray[i] =
        new Array( bits + 1 - initialArray[i].length).join("0") + initialArray[i];
      if (colorBits) {
        //add  around all bits
        initialArray[i] = '' + initialArray[i].split("").join('') + '';
      }
    }

    // initialize array
    if (array) {
      clickHandler.remove(array);
      array.clear();
    }
    array = av.ds.array(initialArray, {indexed: true});
    array.element.css({top: 40});
    array.layout();
    clickHandler.addArray(array, {onSelect:
      function (index) {
        switch (mode.value()) {
          case 0:
            //return true to tell clickHandler to select the item
            return true;
            break;
          case 1:
            extendStackValue("Left", index);
            av.umsg("Select the right endpoint.");
            mode.value(2);
            av.step();
            break;
          case 2:
            extendStackValue("Right", index);
            av.umsg("");
            focusOn(array, getCurrentValue("Left", stack), index, getCurrentValue("Bit", stack));
            mode.value(0);
            exercise.gradeableStep();
            break;
        }
        //disable selecting
        return false;
      }
    });

    //stack
    if (stack) {
      clickHandler.remove(stack);
      stack.clear();
    }
    stack = av.ds.stack({xtransition: 5, ytransition: 25, center: false});
    // stack = av.ds.list({nodegap: 15, layout: "vertical", center: false, autoresize: false});
    stack.element.css({width: 180, position: "absolute"});
    stack.element.css({top: 200, left: av.canvas.width() / 2 - 90});
    stack.addFirst("Bit: "+(bits - 1)+", Left: 0, Right: "+(arraySize - 1));
    stack.layout();

    //mode variable
    //0 when swapping
    //1 when selecting left endpoint
    //2 when selecting right endpoint
    if (mode) {
      mode.clear();
    }
    mode = av.variable(0);

    // clear all the Raphael elements
    av.getSvg().clear();

    // add text
    var font = {
      "font-size": 16,
      "font-family": "Times New Roman",
      "font-weight": "bold"
    };
    var canvasWidth = exercise.jsav.container.find(".jsavcanvas").width();
    av.getSvg().text(canvasWidth / 2, 20, "Table to be sorted").attr(font);
    av.getSvg().text(canvasWidth / 2, 180, "Call Stack").attr(font);

    //hide old umsg messages
    av.umsg("");

    focusOn(array, 0, arraySize - 1, bits - 1);

    return array;
  }

  function modelSolution(jsav) {
    //array
    var modelArray = jsav.ds.array(initialArray, {indexed: true});

    // var modelStack = jsav.ds.list({nodegap: 15, layout: "vertical", center: false, autoresize: false});
    var modelStack = jsav.ds.stack({xtransition: 5, ytransition: 25, center: false});
    modelStack.element.css({width: 180, position: "absolute"});
    modelStack.element.css({top: 100, left: jsav.canvas.width() / 2 - 90});

    jsav.canvas.css({height: 350});

    jsav._undo = [];

    // $(".jsavforward").click(function () {
    //   if (jsav.container.hasClass("jsavplaying")) {
    //     jsav.container.removeClass("jsavplaying");
    //   }
    // });
    // $(".jsavbackward").click(function () {
    //   if (jsav.container.hasClass("jsavplaying")) {
    //     jsav.container.removeClass("jsavplaying");
    //   }
    // });

    //get the bit from the array at index
    //removes possible spans
    function getBit(arr, index, bit) {
      return parseInt(arr.value(index).replace(/<\/?span[^>]*>/g,"").charAt(bits - 1 - bit));
    }

    function modelRadix(bit, left, right) {
      modelStack.addFirst("Bit: " + bit + ", Left: " + left + ", Right: " + right);
      modelStack.layout();

      focusOn(modelArray, left, right, bit);

      //add a step if not first call
      if (left !== 0 || right !== arraySize - 1) {
        jsav.stepOption("grade", true);
        jsav.step();
      } else {
        jsav.displayInit();
      }

      var i = left;
      var j = right;

      while (i < j) {
        while ( i <= right && getBit(modelArray, i, bit) === 0)
          i++;
        while ( j >= left && getBit(modelArray, j, bit) === 1)
          j--;
        if (i < j) {
          modelArray.swap(i, j);
          jsav.stepOption("grade", true);
          jsav.step();
        }
      }

      //call function recursivley for both sides
      if (bit > 0) {
        if (left < j)
          modelRadix(bit - 1, left, j);
        if (right > i)
          modelRadix(bit - 1, i, right);
      }

      //return
      returnClick(modelArray, modelStack);
      jsav.stepOption("grade", true);
      jsav.step();
    }

    modelRadix(bits - 1, 0, arraySize - 1);

    return modelArray;
  }

  //create excercise
  var exercise = av.exercise(modelSolution, initialize, {css: "background-color"}, {feedback: "atend"});
  exercise.reset();

  var $callButton = $("#callButton");
  var $returnButton = $("#returnButton");

  // add buttons if they don't exist
  if ($callButton.length === 0) {
    $callButton = $("<button>Call</button>");
    $("#jsavcontainer .jsavcanvas").append($callButton);
  }
  if ($returnButton.length === 0) {
    $returnButton = $("<button>Return</button>")
    $("#jsavcontainer .jsavcanvas").append($returnButton);
  }

  //position buttons
  $callButton.css({position: "absolute", left: 50, top: 200, width: 100});
  $returnButton.css({position: "absolute", left: 50, top: 230, width: 100});
  //add click handlers
  $callButton.click(function () {
    mode.value(1);
    clickHandler.deselect();
    stack.addFirst("Bit: " + (getCurrentValue("Bit", stack) - 1));
    stack.layout();
    av.umsg("Select the left endpoint.");
    av.step();
  });
  $returnButton.click(function () {
    returnClick(array, stack);
    exercise.gradeableStep();
  });

  //returns the value of Bit, Left or Right of the topmost item of the stack
  function getCurrentValue(name, stack) {
    var result;
    var value;
    if (stack.first()) {
      value = stack.first().value();
    } else {
      value = "Bit: " + bits + ", Left: " + 0 + ", Right: " + (arraySize - 1);
    }
    var parts = value.split(", ");
    parts.forEach(function (val) {
      var newparts = val.split(": ")
      if (newparts[0] === name){
        result = newparts[1];
      }
    });
    return parseInt(result);
  }

  //extends the value of the topmost element on the stack
  function extendStackValue(name, value) {
    var oldvalue = stack.first().value();
    oldvalue += ", " + name + ": " + value;
    stack.first().value(oldvalue);
  }

  //paints all the squares outside of [first, last] grey
  function focusOn(arr, first, last, bit) {
    arr.removeClass(true, "greybg");
    arr.addClass(function (index) {
      return index < first || index > last;
    },
    "greybg");
    if (colorBits && typeof bit === "number") {
      //uncolor all bits
      arr.element.find(".bit").removeClass("coloredbit");
      //color the wanted bits
      arr.element.find(".jsavvalue").find(".bit:eq("+(bits - 1 - bit)+")").addClass("coloredbit");
      if (bit === bits) {
        //remove all bit classes
        for (var i = 0; i < arraySize; i++) {
          var $temp = arr.element.find(".jsavvaluelabel:eq("+i+")");
          $temp.html($temp.text());
        }
      }
      //update the real values of the elements
      for (var i = 0; i < arraySize; i++) {
        var v = arr.element.find("li:eq("+i+") .jsavvaluelabel").html();
        arr.value(i, v);
      }
    }
  }

  //pops the stack and focuses on the previous range
  function returnClick(array, stack) {
    if (stack.size()) {
      stack.removeFirst();
      stack.layout();
    }
    if (stack.size()) {
      focusOn(array,
        getCurrentValue("Left", stack),
        getCurrentValue("Right", stack),
        getCurrentValue("Bit", stack));
    } else {
      focusOn(array, 0, arraySize - 1, bits);
    }
  }

}(jQuery));<commit_msg>Removed misleading comment from radix exchange sort exercise.<commit_after>(function ($) {
  "use strict";
  var arraySize = 10,
    bits = 3,
    colorBits = true,
    initialArray,
    array,
    stack,
    mode,
    av = new JSAV($("#jsavcontainer")),
    clickHandler;

  av.recorded(); // we are not recording an AV with an algorithm

  function initialize() {

    exercise.jsav.container.find(".jsavcanvas").css({height: 350});

    //set up click handler
    if (typeof clickHandler === "undefined") {
      clickHandler = new ClickHandler(av, exercise, {effect: "swap", selectedClass: "selected"});
    }
    clickHandler.reset();

    //generate random infix and insert in the array
    initialArray = JSAV.utils.rand.numKeys(0, Math.pow(2, bits), arraySize);
    for (var i = 0; i < arraySize; i++) {
      //convert into binary
      initialArray[i] = initialArray[i].toString(2);
      //add leading zeros
      initialArray[i] =
        new Array( bits + 1 - initialArray[i].length).join("0") + initialArray[i];
      if (colorBits) {
        //add  around all bits
        initialArray[i] = '' + initialArray[i].split("").join('') + '';
      }
    }

    // initialize array
    if (array) {
      clickHandler.remove(array);
      array.clear();
    }
    array = av.ds.array(initialArray, {indexed: true});
    array.element.css({top: 40});
    array.layout();
    clickHandler.addArray(array, {onSelect:
      function (index) {
        switch (mode.value()) {
          case 0:
            //return true to tell clickHandler to select the item
            return true;
            break;
          case 1:
            extendStackValue("Left", index);
            av.umsg("Select the right endpoint.");
            mode.value(2);
            av.step();
            break;
          case 2:
            extendStackValue("Right", index);
            av.umsg("");
            focusOn(array, getCurrentValue("Left", stack), index, getCurrentValue("Bit", stack));
            mode.value(0);
            exercise.gradeableStep();
            break;
        }
        //disable selecting
        return false;
      }
    });

    //stack
    if (stack) {
      clickHandler.remove(stack);
      stack.clear();
    }
    stack = av.ds.stack({xtransition: 5, ytransition: 25, center: false});
    // stack = av.ds.list({nodegap: 15, layout: "vertical", center: false, autoresize: false});
    stack.element.css({width: 180, position: "absolute"});
    stack.element.css({top: 200, left: av.canvas.width() / 2 - 90});
    stack.addFirst("Bit: "+(bits - 1)+", Left: 0, Right: "+(arraySize - 1));
    stack.layout();

    //mode variable
    //0 when swapping
    //1 when selecting left endpoint
    //2 when selecting right endpoint
    if (mode) {
      mode.clear();
    }
    mode = av.variable(0);

    // clear all the Raphael elements
    av.getSvg().clear();

    // add text
    var font = {
      "font-size": 16,
      "font-family": "Times New Roman",
      "font-weight": "bold"
    };
    var canvasWidth = exercise.jsav.container.find(".jsavcanvas").width();
    av.getSvg().text(canvasWidth / 2, 20, "Table to be sorted").attr(font);
    av.getSvg().text(canvasWidth / 2, 180, "Call Stack").attr(font);

    //hide old umsg messages
    av.umsg("");

    focusOn(array, 0, arraySize - 1, bits - 1);

    return array;
  }

  function modelSolution(jsav) {
    //array
    var modelArray = jsav.ds.array(initialArray, {indexed: true});

    // var modelStack = jsav.ds.list({nodegap: 15, layout: "vertical", center: false, autoresize: false});
    var modelStack = jsav.ds.stack({xtransition: 5, ytransition: 25, center: false});
    modelStack.element.css({width: 180, position: "absolute"});
    modelStack.element.css({top: 100, left: jsav.canvas.width() / 2 - 90});

    jsav.canvas.css({height: 350});

    jsav._undo = [];

    // $(".jsavforward").click(function () {
    //   if (jsav.container.hasClass("jsavplaying")) {
    //     jsav.container.removeClass("jsavplaying");
    //   }
    // });
    // $(".jsavbackward").click(function () {
    //   if (jsav.container.hasClass("jsavplaying")) {
    //     jsav.container.removeClass("jsavplaying");
    //   }
    // });

    //get the bit from the array at index
    //removes possible spans
    function getBit(arr, index, bit) {
      return parseInt(arr.value(index).replace(/<\/?span[^>]*>/g,"").charAt(bits - 1 - bit));
    }

    function modelRadix(bit, left, right) {
      modelStack.addFirst("Bit: " + bit + ", Left: " + left + ", Right: " + right);
      modelStack.layout();

      focusOn(modelArray, left, right, bit);

      //add a step if not first call
      if (left !== 0 || right !== arraySize - 1) {
        jsav.stepOption("grade", true);
        jsav.step();
      } else {
        jsav.displayInit();
      }

      var i = left;
      var j = right;

      while (i < j) {
        while ( i <= right && getBit(modelArray, i, bit) === 0)
          i++;
        while ( j >= left && getBit(modelArray, j, bit) === 1)
          j--;
        if (i < j) {
          modelArray.swap(i, j);
          jsav.stepOption("grade", true);
          jsav.step();
        }
      }

      //call function recursivley for both sides
      if (bit > 0) {
        if (left < j)
          modelRadix(bit - 1, left, j);
        if (right > i)
          modelRadix(bit - 1, i, right);
      }

      //return
      returnClick(modelArray, modelStack);
      jsav.stepOption("grade", true);
      jsav.step();
    }

    modelRadix(bits - 1, 0, arraySize - 1);

    return modelArray;
  }

  //create excercise
  var exercise = av.exercise(modelSolution, initialize, {css: "background-color"}, {feedback: "atend"});
  exercise.reset();

  var $callButton = $("#callButton");
  var $returnButton = $("#returnButton");

  // add buttons if they don't exist
  if ($callButton.length === 0) {
    $callButton = $("<button>Call</button>");
    $("#jsavcontainer .jsavcanvas").append($callButton);
  }
  if ($returnButton.length === 0) {
    $returnButton = $("<button>Return</button>")
    $("#jsavcontainer .jsavcanvas").append($returnButton);
  }

  //position buttons
  $callButton.css({position: "absolute", left: 50, top: 200, width: 100});
  $returnButton.css({position: "absolute", left: 50, top: 230, width: 100});
  //add click handlers
  $callButton.click(function () {
    mode.value(1);
    clickHandler.deselect();
    stack.addFirst("Bit: " + (getCurrentValue("Bit", stack) - 1));
    stack.layout();
    av.umsg("Select the left endpoint.");
    av.step();
  });
  $returnButton.click(function () {
    returnClick(array, stack);
    exercise.gradeableStep();
  });

  //returns the value of Bit, Left or Right of the topmost item of the stack
  function getCurrentValue(name, stack) {
    var result;
    var value;
    if (stack.first()) {
      value = stack.first().value();
    } else {
      value = "Bit: " + bits + ", Left: " + 0 + ", Right: " + (arraySize - 1);
    }
    var parts = value.split(", ");
    parts.forEach(function (val) {
      var newparts = val.split(": ")
      if (newparts[0] === name){
        result = newparts[1];
      }
    });
    return parseInt(result);
  }

  //extends the value of the topmost element on the stack
  function extendStackValue(name, value) {
    var oldvalue = stack.first().value();
    oldvalue += ", " + name + ": " + value;
    stack.first().value(oldvalue);
  }

  //paints all the squares outside of [first, last] grey
  function focusOn(arr, first, last, bit) {
    arr.removeClass(true, "greybg");
    arr.addClass(function (index) {
      return index < first || index > last;
    },
    "greybg");
    if (colorBits && typeof bit === "number") {
      //uncolor all bits
      arr.element.find(".bit").removeClass("coloredbit");
      //color the wanted bits
      arr.element.find(".jsavvalue").find(".bit:eq("+(bits - 1 - bit)+")").addClass("coloredbit");
      if (bit === bits) {
        //remove all bit classes
        for (var i = 0; i < arraySize; i++) {
          var $temp = arr.element.find(".jsavvaluelabel:eq("+i+")");
          $temp.html($temp.text());
        }
      }
      //update the real values of the elements
      for (var i = 0; i < arraySize; i++) {
        var v = arr.element.find("li:eq("+i+") .jsavvaluelabel").html();
        arr.value(i, v);
      }
    }
  }

  //pops the stack and focuses on the previous range
  function returnClick(array, stack) {
    if (stack.size()) {
      stack.removeFirst();
      stack.layout();
    }
    if (stack.size()) {
      focusOn(array,
        getCurrentValue("Left", stack),
        getCurrentValue("Right", stack),
        getCurrentValue("Bit", stack));
    } else {
      focusOn(array, 0, arraySize - 1, bits);
    }
  }

}(jQuery));<|endoftext|>


<commit_before>/***********************************************************************

    Check in, check uit

    Copyright 2010, Martijn Vermaat <martijn@vermaat.name>

      http://checkincheckuit.nl
      http://svn.vermaat.name/checkincheckuit

    Licensed under the MIT license, see the LICENSE file

      http://en.wikipedia.org/wiki/Mit_license

    Some of the Twitter handling code was adapted from the jQuery
    plugin Tweet!, found at http://tweet.seaofclouds.com/

    This code uses:
      jQuery          http://jquery.com
      jQuery Timers   http://plugins.jquery.com/project/timers

***********************************************************************/

$(document).ready(function() {

    var twitterSearch = 'chipkaart OR ovchipkaart';
    var twitterCount = 20;
    var twitterDisplayTime = 1000 * 8;
    var twitterFadeTime = 800;
    /*
      There seems to be a bug, where animations are not smooth when the
      interval is specified as a string, e.g. '5s'.
    */
    var twitterReloadTime = 1000 * 60 * 5;

    var deliciousUser = 'checkuitcheckin';
    var deliciousTag = 'nieuws';
    var deliciousCount = 10;

    var twitterUrl = 'http://search.twitter.com/search?&q='
        + encodeURIComponent(twitterSearch);
    var twitterApiUrl = 'http://search.twitter.com/search.json?&q='
        + encodeURIComponent(twitterSearch)
        + '&rpp=' + twitterCount + '&callback=?';
    var initialTweet = true;
    var tweets = [];
    var currentTweet = 0;

    var deliciousUrl = 'http://delicious.com/' + deliciousUser
        + '/' + deliciousTag;
    var deliciousApiUrl = 'http://feeds.delicious.com/v2/json/'
        + deliciousUser + '/' + deliciousTag
        + '?count=' + deliciousCount + '&callback=?';

    // Load Twitter search results and call callback function
    function updateTwitter(callback) {

        // http://apiwiki.twitter.com/Return-Values

        $.getJSON(twitterApiUrl, function(data) {

            $.each((data.results || data), function(i, tweet) {
                var user = tweet.from_user || tweet.user.screen_name;
                tweets.push({
                    text    : twittify(tweet.text),
                    user    : user,
                    date    : relativeTime(tweet.created_at),
                    url     : 'http://twitter.com/' + user
                              + '/statuses/' + tweet.id,
                    userUrl : 'http://twitter.com/' + user,
                });
            });

            currentTweet = 0;
            if (callback) callback();

        });

    }

    // Load Delicious links and show them on the page
    function updateDelicious() {

        // http://delicious.com/help/feeds

        $.getJSON(deliciousApiUrl, function(data) {
            var list = $('
').appendTo($('#delicious'));
            $.each((data.results || data), function(i, item) {
                list.append($('
').text(shortDate(item.dt)));
                list.append($('
').append(
                    $('').attr('href', item.u).text(item.d)));
            });
            $('#delicious').append($('
').append(
                $('Meer nieuwsberichten...').attr('href', deliciousUrl)));
        });

    }

    // Advance to the next twee
    function showNextTweet() {
        if (tweets.length < 1) return;
        currentTweet = (currentTweet + 1) % tweets.length;
        showTweet();
    }

    // Like the function above, with a minor difference
    function showPreviousTweet() {
        if (tweets.length < 1) return;
        currentTweet = (tweets.length + currentTweet - 1) % tweets.length;
        showTweet();
    }

    /*
      Show the current tweet on the page. If this is the first twee we show,
      do just that. Otherwise fadeout the old tweet and fadein the new.
    */
    function showTweet() {

        var show = function() {
            var tweet = tweets[currentTweet];
            var pane = $('#content .tweet');
            /*
              The documentation (and experience) tells us we get the tweet
              text 'escaped and HTML encoded'.
              I still don't like to .html() this into the page, but being more
              cautious would make twittify() much more involved.

              http://apiwiki.twitter.com/Return-Values
            */
            pane.find('.text').html(tweet.text);
            pane.find('h2').empty().append(
                $('').attr('href', tweet.userUrl).text('@' + tweet.user));
            pane.find('h2').append(
                $('').attr('href', tweet.url).text(tweet.date).addClass(
                    'date'));
        }

        if (initialTweet)
            show();
        else
            $("#content .tweet").fadeOut(twitterFadeTime, function () {
                show();
                $("#content .tweet").fadeIn(twitterFadeTime);
            });

        initialTweet = false;

    }

    // Setup the Twitter stream
    function setupTwitter() {

        // Get tweets, show the first one, and skip to next tweet every 5 seconds
        updateTwitter(function() {

            if (tweets.length > 0)
                showTweet();

            if (tweets.length > 1) {

                $(document).everyTime(twitterDisplayTime, showNextTweet);

                // We add navigation links and reset the skip timer on click
                $('#content').append($('
').append(
                    $('
<
').click(function() {
                        showPreviousTweet();
                        $(document).stopTime();
                        $(document).everyTime(twitterDisplayTime, showNextTweet);
                    })
                ).append(
                    $('
>
').click(function() {
                        showNextTweet();
                        $(document).stopTime();
                        $(document).everyTime(twitterDisplayTime, showNextTweet);
                    })
                ));

            }

        });

        // Every 5 minutes, get tweets and start at the latest tweet again
        $(document).everyTime(twitterReloadTime, updateTwitter);

        // Show link to Twitter search results
        $('#content').append($('
').append(
            $('meer...').attr('href', twitterUrl)));

        // Pause auto advancing of tweets on mouse hover
        $('#content .tweet').hover(function() {
            $(document).stopTime();
        }, function() {
            if (tweets.length > 1) {
                showNextTweet();
                $(document).everyTime(twitterDisplayTime, showNextTweet);
            }
        });

    }

    // Show current date
    $('.now').text(shortDate());

    // Show Delicious links
    updateDelicious();

    // If this is the index page, setup the Twitter stream
    if ($('body').hasClass('index'))
        setupTwitter();

});

// Create links for urls, users, and hashtags
function twittify(tweet) {

    var linkUrl = function(s) {
        var returning = [];
        var regexp = /((ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?)/gi;
        return s.replace(regexp,"$1");
    }

    var linkUser = function(s) {
        var returning = [];
        var regexp = /[\@]+([A-Za-z0-9-_]+)/gi;
        return s.replace(regexp,"@$1");
    }

    var linkHash = function(s) {
        var returning = [];
        var regexp = / [\#]+([A-Za-z0-9-_]+)/gi;
        return s.replace(regexp, ' #$1');
    }

    return linkHash(linkUser(linkUrl(tweet)));

}

/*
  The non-search twitter APIs return inconsistently-formatted dates, which
  Date.parse cannot handle in IE. We therefore perform the following
  transformation:

  Wed Apr 29 08:53:31 +0000 2009
  =>
  Wed, Apr 29 2009 08:53:31 +0000
*/
function parseDate(s) {
    return Date.parse(s.replace(/^([a-z]{3})( [a-z]{3} \d\d?)(.*)( \d{4})$/i,
                                '$1,$2$4$3'));
}

// Turn a date/time string into a relative time
function relativeTime(time) {

    var parsedDate = parseDate(time);
    var relativeTo = (arguments.length > 1) ? arguments[1] : new Date();
    var delta = parseInt((relativeTo.getTime() - parsedDate) / 1000);

    var pluralize = function(singular, n) {
        var plurals = {
            'minuut' : 'minuten',
            'uur'    : 'uur',
            'dag'    : 'dagen'
        };
        return '' + n + ' ' + (n == 1 ? singular : plurals[singular]);
    };

    if (delta < 60)
        return 'seconden geleden';
    else if (delta < (60 * 60))
        return pluralize('minuut', parseInt(delta / 60)) + ' geleden';
    else if (delta < (24 * 60 * 60))
        return pluralize('uur', parseInt(delta / 3600)) + ' geleden';
    else
        return pluralize('dag', parseInt(delta / 86400)) + ' geleden';

}

// Create a short description from a date string
function shortDate(s) {

    var months = ['jan', 'feb', 'mrt', 'apr', 'mei', 'jun',
                  'jul', 'aug', 'sep', 'okt', 'nov', 'dec'];

    if (!s)
        return (new Date).getDate() + ' ' + months[(new Date).getMonth() + 1];

    var d = s.match(/^\d{4}-(\d{2})-(\d{2}).*$/);
    if (d != null)
        return d[2] + ' ' + months[parseInt(d[1]) - 1];

    return '';

}
<commit_msg>Fix: update tweets (vervang oude door nieuwe, niet nieuwe toevoegen). Niet direct naar volgende tweet bij hover-out.<commit_after>/***********************************************************************

    Check in, check uit

    Copyright 2010, Martijn Vermaat <martijn@vermaat.name>

      http://checkincheckuit.nl
      http://svn.vermaat.name/checkincheckuit

    Licensed under the MIT license, see the LICENSE file

      http://en.wikipedia.org/wiki/Mit_license

    Some of the Twitter handling code was adapted from the jQuery
    plugin Tweet!, found at http://tweet.seaofclouds.com/

    This code uses:
      jQuery          http://jquery.com
      jQuery Timers   http://plugins.jquery.com/project/timers

***********************************************************************/

$(document).ready(function() {

    var twitterSearch = 'chipkaart OR ovchipkaart';
    var twitterCount = 20;
    var twitterDisplayTime = 1000 * 8;
    var twitterFadeTime = 800;
    /*
      There seems to be a bug, where animations are not smooth when the
      interval is specified as a string, e.g. '5s'.
    */
    var twitterReloadTime = 1000 * 60 * 5;

    var deliciousUser = 'checkuitcheckin';
    var deliciousTag = 'nieuws';
    var deliciousCount = 10;

    var twitterUrl = 'http://search.twitter.com/search?&q='
        + encodeURIComponent(twitterSearch);
    var twitterApiUrl = 'http://search.twitter.com/search.json?&q='
        + encodeURIComponent(twitterSearch)
        + '&rpp=' + twitterCount + '&callback=?';
    var initialTweet = true;
    var tweets = [];
    var currentTweet = 0;

    var deliciousUrl = 'http://delicious.com/' + deliciousUser
        + '/' + deliciousTag;
    var deliciousApiUrl = 'http://feeds.delicious.com/v2/json/'
        + deliciousUser + '/' + deliciousTag
        + '?count=' + deliciousCount + '&callback=?';

    // Load Twitter search results and call callback function
    function updateTwitter(callback) {

        // http://apiwiki.twitter.com/Return-Values

        $.getJSON(twitterApiUrl, function(data) {

            currentTweet = 0;
            tweets = [];

            $.each((data.results || data), function(i, tweet) {
                var user = tweet.from_user || tweet.user.screen_name;
                tweets.push({
                    text    : twittify(tweet.text),
                    user    : user,
                    date    : relativeTime(tweet.created_at),
                    url     : 'http://twitter.com/' + user
                              + '/statuses/' + tweet.id,
                    userUrl : 'http://twitter.com/' + user,
                });
            });

            if (callback) callback();

        });

    }

    // Load Delicious links and show them on the page
    function updateDelicious() {

        // http://delicious.com/help/feeds

        $.getJSON(deliciousApiUrl, function(data) {
            var list = $('
').appendTo($('#delicious'));
            $.each((data.results || data), function(i, item) {
                list.append($('
').text(shortDate(item.dt)));
                list.append($('
').append(
                    $('').attr('href', item.u).text(item.d)));
            });
            $('#delicious').append($('
').append(
                $('Meer nieuwsberichten...').attr('href', deliciousUrl)));
        });

    }

    // Advance to the next twee
    function showNextTweet() {
        if (tweets.length < 1) return;
        currentTweet = (currentTweet + 1) % tweets.length;
        showTweet();
    }

    // Like the function above, with a minor difference
    function showPreviousTweet() {
        if (tweets.length < 1) return;
        currentTweet = (tweets.length + currentTweet - 1) % tweets.length;
        showTweet();
    }

    /*
      Show the current tweet on the page. If this is the first twee we show,
      do just that. Otherwise fadeout the old tweet and fadein the new.
    */
    function showTweet() {

        var show = function() {
            var tweet = tweets[currentTweet];
            var pane = $('#content .tweet');
            /*
              The documentation (and experience) tells us we get the tweet
              text 'escaped and HTML encoded'.
              I still don't like to .html() this into the page, but being more
              cautious would make twittify() much more involved.

              http://apiwiki.twitter.com/Return-Values
            */
            pane.find('.text').html(tweet.text);
            pane.find('h2').empty().append(
                $('').attr('href', tweet.userUrl).text('@' + tweet.user));
            pane.find('h2').append(
                $('').attr('href', tweet.url).text(tweet.date).addClass(
                    'date'));
        }

        if (initialTweet)
            show();
        else
            $("#content .tweet").fadeOut(twitterFadeTime, function () {
                show();
                $("#content .tweet").fadeIn(twitterFadeTime);
            });

        initialTweet = false;

    }

    // Setup the Twitter stream
    function setupTwitter() {

        // Get tweets, show the first one, and skip to next tweet every 5 seconds
        updateTwitter(function() {

            if (tweets.length > 0)
                showTweet();

            if (tweets.length > 1) {

                $(document).everyTime(twitterDisplayTime, showNextTweet);

                // We add navigation links and reset the skip timer on click
                $('#content').append($('
').append(
                    $('
<
').click(function() {
                        showPreviousTweet();
                        $(document).stopTime();
                        $(document).everyTime(twitterDisplayTime, showNextTweet);
                    })
                ).append(
                    $('
>
').click(function() {
                        showNextTweet();
                        $(document).stopTime();
                        $(document).everyTime(twitterDisplayTime, showNextTweet);
                    })
                ));

            }

        });

        // Every 5 minutes, get tweets and start at the latest tweet again
        $(document).everyTime(twitterReloadTime, updateTwitter);

        // Show link to Twitter search results
        $('#content').append($('
').append(
            $('meer...').attr('href', twitterUrl)));

        // Pause auto advancing of tweets on mouse hover
        $('#content .tweet').hover(function() {
            $(document).stopTime();
        }, function() {
            if (tweets.length > 1) {
                // If we directly want to advance on hover-out, uncomment this
                //showNextTweet();
                $(document).everyTime(twitterDisplayTime, showNextTweet);
            }
        });

    }

    // Show current date
    $('.now').text(shortDate());

    // Show Delicious links
    updateDelicious();

    // If this is the index page, setup the Twitter stream
    if ($('body').hasClass('index'))
        setupTwitter();

});

// Create links for urls, users, and hashtags
function twittify(tweet) {

    var linkUrl = function(s) {
        var returning = [];
        var regexp = /((ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?)/gi;
        return s.replace(regexp,"$1");
    }

    var linkUser = function(s) {
        var returning = [];
        var regexp = /[\@]+([A-Za-z0-9-_]+)/gi;
        return s.replace(regexp,"@$1");
    }

    var linkHash = function(s) {
        var returning = [];
        var regexp = / [\#]+([A-Za-z0-9-_]+)/gi;
        return s.replace(regexp, ' #$1');
    }

    return linkHash(linkUser(linkUrl(tweet)));

}

/*
  The non-search twitter APIs return inconsistently-formatted dates, which
  Date.parse cannot handle in IE. We therefore perform the following
  transformation:

  Wed Apr 29 08:53:31 +0000 2009
  =>
  Wed, Apr 29 2009 08:53:31 +0000
*/
function parseDate(s) {
    return Date.parse(s.replace(/^([a-z]{3})( [a-z]{3} \d\d?)(.*)( \d{4})$/i,
                                '$1,$2$4$3'));
}

// Turn a date/time string into a relative time
function relativeTime(time) {

    var parsedDate = parseDate(time);
    var relativeTo = (arguments.length > 1) ? arguments[1] : new Date();
    var delta = parseInt((relativeTo.getTime() - parsedDate) / 1000);

    var pluralize = function(singular, n) {
        var plurals = {
            'minuut' : 'minuten',
            'uur'    : 'uur',
            'dag'    : 'dagen'
        };
        return '' + n + ' ' + (n == 1 ? singular : plurals[singular]);
    };

    if (delta < 60)
        return 'seconden geleden';
    else if (delta < (60 * 60))
        return pluralize('minuut', parseInt(delta / 60)) + ' geleden';
    else if (delta < (24 * 60 * 60))
        return pluralize('uur', parseInt(delta / 3600)) + ' geleden';
    else
        return pluralize('dag', parseInt(delta / 86400)) + ' geleden';

}

// Create a short description from a date string
function shortDate(s) {

    var months = ['jan', 'feb', 'mrt', 'apr', 'mei', 'jun',
                  'jul', 'aug', 'sep', 'okt', 'nov', 'dec'];

    if (!s)
        return (new Date).getDate() + ' ' + months[(new Date).getMonth() + 1];

    var d = s.match(/^\d{4}-(\d{2})-(\d{2}).*$/);
    if (d != null)
        return d[2] + ' ' + months[parseInt(d[1]) - 1];

    return '';

}
<|endoftext|>


<commit_before>fiveSidedDie.js
<commit_msg>add desc<commit_after>// You have a function rand7() that generates a random integer from 1 to 7.
// Use it to write a function rand5() that generates a random integer from 1 to 5.

// rand7() returns each integer with equal probability.
// rand5() must also return each integer with equal probability.
<|endoftext|>


<commit_before> Linked List Cycle II

Given a linked list, return the node where the cycle begins. If there is no cycle, return null.

Note: Do not modify the linked list.

Follow up:
Can you solve it without using extra space?

/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */

<commit_msg>add params<commit_after> Linked List Cycle II

Given a linked list, return the node where the cycle begins. If there is no cycle, return null.

Note: Do not modify the linked list.

Follow up:
Can you solve it without using extra space?

/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */

/**
 * @param {ListNode} head
 * @return {ListNode}
 */
<|endoftext|>


<commit_before>(function() {
  'use strict';
  /**
   * Creates and maintains a synchronized object. This constructor should not be
   * manually invoked. Instead, one should create a $firebase object and call $asObject
   * on it:  $firebase( firebaseRef ).$asObject();
   *
   * Internally, the $firebase object depends on this class to provide 2 methods, which it invokes
   * to notify the object whenever a change has been made at the server:
   *    $$updated - called whenever a change occurs (a value event from Firebase)
   *    $$error - called when listeners are canceled due to a security error
   *
   * Instead of directly modifying this class, one should generally use the $extendFactory
   * method to add or change how methods behave:
   *
   *

   * var NewFactory = $FirebaseObject.$extendFactory({
   *    // add a new method to the prototype
   *    foo: function() { return 'bar'; },
   * });
   *

   *
   * And then the new factory can be used by passing it as an argument:
   * $firebase( firebaseRef, {objectFactory: NewFactory}).$asObject();
   */
  angular.module('firebase').factory('$FirebaseObject', [
    '$parse', '$firebaseUtils', '$log', '$interval',
    function($parse, $firebaseUtils, $log) {
      /**
       * This constructor should probably never be called manually. It is used internally by
       * $firebase.$asObject().
       *
       * @param $firebase
       * @param {Function} destroyFn invoking this will cancel all event listeners and stop
       *                   notifications from being delivered to $$updated and $$error
       * @param readyPromise resolved when the initial data downloaded from Firebase
       * @returns {FirebaseObject}
       * @constructor
       */
      function FirebaseObject($firebase, destroyFn, readyPromise) {
        // These are private config props and functions used internally
        // they are collected here to reduce clutter in console.log and forEach
        this.$$conf = {
          promise: readyPromise,
          inst: $firebase,
          binding: new ThreeWayBinding(this),
          destroyFn: destroyFn,
          listeners: []
        };

        // this bit of magic makes $$conf non-enumerable and non-configurable
        // and non-writable (its properties are still writable but the ref cannot be replaced)
        // we declare it above so the IDE can relax
        Object.defineProperty(this, '$$conf', {
          value: this.$$conf
        });

        this.$id = $firebaseUtils.getKey($firebase.$ref().ref());
        this.$priority = null;

        $firebaseUtils.applyDefaults(this, this.$$defaults);
      }

      FirebaseObject.prototype = {
        /**
         * Saves all data on the FirebaseObject back to Firebase.
         * @returns a promise which will resolve after the save is completed.
         */
        $save: function () {
          var self = this;
          return self.$inst().$set($firebaseUtils.toJSON(self))
            .then(function(ref) {
              self.$$notify();
              return ref;
            });
        },

        /**
         * Removes all keys from the FirebaseObject and also removes
         * the remote data from the server.
         *
         * @returns a promise which will resolve after the op completes
         */
        $remove: function() {
          var self = this;
          $firebaseUtils.trimKeys(this, {});
          this.$value = null;
          return self.$inst().$remove().then(function(ref) {
            self.$$notify();
            return ref;
          });
        },

        /**
         * The loaded method is invoked after the initial batch of data arrives from the server.
         * When this resolves, all data which existed prior to calling $asObject() is now cached
         * locally in the object.
         *
         * As a shortcut is also possible to pass resolve/reject methods directly into this
         * method just as they would be passed to .then()
         *
         * @param {Function} resolve
         * @param {Function} reject
         * @returns a promise which resolves after initial data is downloaded from Firebase
         */
        $loaded: function(resolve, reject) {
          var promise = this.$$conf.promise;
          if (arguments.length) {
            // allow this method to be called just like .then
            // by passing any arguments on to .then
            promise = promise.then.call(promise, resolve, reject);
          }
          return promise;
        },

        /**
         * @returns the original $firebase object used to create this object.
         */
        $inst: function () {
          return this.$$conf.inst;
        },

        /**
         * Creates a 3-way data sync between this object, the Firebase server, and a
         * scope variable. This means that any changes made to the scope variable are
         * pushed to Firebase, and vice versa.
         *
         * If scope emits a $destroy event, the binding is automatically severed. Otherwise,
         * it is possible to unbind the scope variable by using the `unbind` function
         * passed into the resolve method.
         *
         * Can only be bound to one scope variable at a time. If a second is attempted,
         * the promise will be rejected with an error.
         *
         * @param {object} scope
         * @param {string} varName
         * @returns a promise which resolves to an unbind method after data is set in scope
         */
        $bindTo: function (scope, varName) {
          var self = this;
          return self.$loaded().then(function () {
            return self.$$conf.binding.bindTo(scope, varName);
          });
        },

        /**
         * Listeners passed into this method are notified whenever a new change is received
         * from the server. Each invocation is sent an object containing
         * { type: 'updated', key: 'my_firebase_id' }
         *
         * This method returns an unbind function that can be used to detach the listener.
         *
         * @param {Function} cb
         * @param {Object} [context]
         * @returns {Function} invoke to stop observing events
         */
        $watch: function (cb, context) {
          var list = this.$$conf.listeners;
          list.push([cb, context]);
          // an off function for cancelling the listener
          return function () {
            var i = list.findIndex(function (parts) {
              return parts[0] === cb && parts[1] === context;
            });
            if (i > -1) {
              list.splice(i, 1);
            }
          };
        },

        /**
         * Informs $firebase to stop sending events and clears memory being used
         * by this object (delete's its local content).
         */
        $destroy: function (err) {
          var self = this;
          if (!self.$isDestroyed) {
            self.$isDestroyed = true;
            self.$$conf.binding.destroy();
            $firebaseUtils.each(self, function (v, k) {
              delete self[k];
            });
            self.$$conf.destroyFn(err);
          }
        },

        /**
         * Called by $firebase whenever an item is changed at the server.
         * This method must exist on any objectFactory passed into $firebase.
         *
         * It should return true if any changes were made, otherwise `$$notify` will
         * not be invoked.
         *
         * @param {object} snap a Firebase snapshot
         * @return {boolean} true if any changes were made.
         */
        $$updated: function (snap) {
          // applies new data to this object
          var changed = $firebaseUtils.updateRec(this, snap);
          // applies any defaults set using $$defaults
          $firebaseUtils.applyDefaults(this, this.$$defaults);
          // returning true here causes $$notify to be triggered
          return changed;
        },

        /**
         * Called whenever a security error or other problem causes the listeners to become
         * invalid. This is generally an unrecoverable error.
         * @param {Object} err which will have a `code` property and possibly a `message`
         */
        $$error: function (err) {
          // prints an error to the console (via Angular's logger)
          $log.error(err);
          // frees memory and cancels any remaining listeners
          this.$destroy(err);
        },

        /**
         * Called internally by $bindTo when data is changed in $scope.
         * Should apply updates to this record but should not call
         * notify().
         */
        $$scopeUpdated: function(newData) {
          // we use a one-directional loop to avoid feedback with 3-way bindings
          // since set() is applied locally anyway, this is still performant
          return this.$inst().$set($firebaseUtils.toJSON(newData));
        },

        /**
         * Updates any bound scope variables and
         * notifies listeners registered with $watch
         */
        $$notify: function() {
          var self = this, list = this.$$conf.listeners.slice();
          // be sure to do this after setting up data and init state
          angular.forEach(list, function (parts) {
            parts[0].call(parts[1], {event: 'value', key: self.$id});
          });
        },

        /**
         * Overrides how Angular.forEach iterates records on this object so that only
         * fields stored in Firebase are part of the iteration. To include meta fields like
         * $id and $priority in the iteration, utilize for(key in obj) instead.
         */
        forEach: function(iterator, context) {
          return $firebaseUtils.each(this, iterator, context);
        }
      };

      /**
       * This method allows FirebaseObject to be copied into a new factory. Methods passed into this
       * function will be added onto the object's prototype. They can override existing methods as
       * well.
       *
       * In addition to passing additional methods, it is also possible to pass in a class function.
       * The prototype on that class function will be preserved, and it will inherit from
       * FirebaseObject. It's also possible to do both, passing a class to inherit and additional
       * methods to add onto the prototype.
       *
       * Once a factory is obtained by this method, it can be passed into $firebase as the
       * `objectFactory` parameter:
       *
       *

       * var MyFactory = $FirebaseObject.$extendFactory({
       *    // add a method onto the prototype that prints a greeting
       *    getGreeting: function() {
       *       return 'Hello ' + this.first_name + ' ' + this.last_name + '!';
       *    }
       * });
       *
       * // use our new factory in place of $FirebaseObject
       * var obj = $firebase(ref, {objectFactory: MyFactory}).$asObject();
       *

       *
       * @param {Function} [ChildClass] a child class which should inherit FirebaseObject
       * @param {Object} [methods] a list of functions to add onto the prototype
       * @returns {Function} a new factory suitable for use with $firebase
       */
      FirebaseObject.$extendFactory = function(ChildClass, methods) {
        if( arguments.length === 1 && angular.isObject(ChildClass) ) {
          methods = ChildClass;
          ChildClass = function() { FirebaseObject.apply(this, arguments); };
        }
        return $firebaseUtils.inherit(ChildClass, FirebaseObject, methods);
      };

      /**
       * Creates a three-way data binding on a scope variable.
       *
       * @param {FirebaseObject} rec
       * @returns {*}
       * @constructor
       */
      function ThreeWayBinding(rec) {
        this.subs = [];
        this.scope = null;
        this.key = null;
        this.rec = rec;
      }

      ThreeWayBinding.prototype = {
        assertNotBound: function(varName) {
          if( this.scope ) {
            var msg = 'Cannot bind to ' + varName + ' because this instance is already bound to ' +
              this.key + '; one binding per instance ' +
              '(call unbind method or create another $firebase instance)';
            $log.error(msg);
            return $firebaseUtils.reject(msg);
          }
        },

        bindTo: function(scope, varName) {
          function _bind(self) {
            var sending = false;
            var parsed = $parse(varName);
            var rec = self.rec;
            self.scope = scope;
            self.varName = varName;

            function equals(rec) {
              var parsed = getScope();
              var newData = $firebaseUtils.scopeData(rec);
              return angular.equals(parsed, newData) &&
                parsed.$priority === rec.$priority &&
                parsed.$value === rec.$value;
            }

            function getScope() {
              return $firebaseUtils.scopeData(parsed(scope));
            }

            function setScope(rec) {
              parsed.assign(scope, $firebaseUtils.scopeData(rec));
            }

            var send = $firebaseUtils.debounce(function() {
              rec.$$scopeUpdated(getScope())
                ['finally'](function() { sending = false; });
            }, 50, 500);

            var scopeUpdated = function() {
              if( !equals(rec) ) {
                sending = true;
                send();
              }
            };

            var recUpdated = function() {
              if( !sending && !equals(rec) ) {
                setScope(rec);
              }
            };

            // $watch will not check any vars prefixed with $, so we
            // manually check $priority and $value using this method
            function checkMetaVars() {
              var dat = parsed(scope);
              if( dat.$value !== rec.$value || dat.$priority !== rec.$priority ) {
                scopeUpdated();
              }
            }

            self.subs.push(scope.$watch(checkMetaVars));

            setScope(rec);
            self.subs.push(scope.$on('$destroy', self.unbind.bind(self)));

            // monitor scope for any changes
            self.subs.push(scope.$watch(varName, scopeUpdated, true));

            // monitor the object for changes
            self.subs.push(rec.$watch(recUpdated));

            return self.unbind.bind(self);
          }

          return this.assertNotBound(varName) || _bind(this);
        },

        unbind: function() {
          if( this.scope ) {
            angular.forEach(this.subs, function(unbind) {
              unbind();
            });
            this.subs = [];
            this.scope = null;
            this.key = null;
          }
        },

        destroy: function() {
          this.unbind();
          this.rec = null;
        }
      };

      return FirebaseObject;
    }
  ]);
})();
<commit_msg>fix: primitive to object transition<commit_after>(function() {
  'use strict';
  /**
   * Creates and maintains a synchronized object. This constructor should not be
   * manually invoked. Instead, one should create a $firebase object and call $asObject
   * on it:  $firebase( firebaseRef ).$asObject();
   *
   * Internally, the $firebase object depends on this class to provide 2 methods, which it invokes
   * to notify the object whenever a change has been made at the server:
   *    $$updated - called whenever a change occurs (a value event from Firebase)
   *    $$error - called when listeners are canceled due to a security error
   *
   * Instead of directly modifying this class, one should generally use the $extendFactory
   * method to add or change how methods behave:
   *
   *

   * var NewFactory = $FirebaseObject.$extendFactory({
   *    // add a new method to the prototype
   *    foo: function() { return 'bar'; },
   * });
   *

   *
   * And then the new factory can be used by passing it as an argument:
   * $firebase( firebaseRef, {objectFactory: NewFactory}).$asObject();
   */
  angular.module('firebase').factory('$FirebaseObject', [
    '$parse', '$firebaseUtils', '$log', '$interval',
    function($parse, $firebaseUtils, $log) {
      /**
       * This constructor should probably never be called manually. It is used internally by
       * $firebase.$asObject().
       *
       * @param $firebase
       * @param {Function} destroyFn invoking this will cancel all event listeners and stop
       *                   notifications from being delivered to $$updated and $$error
       * @param readyPromise resolved when the initial data downloaded from Firebase
       * @returns {FirebaseObject}
       * @constructor
       */
      function FirebaseObject($firebase, destroyFn, readyPromise) {
        // These are private config props and functions used internally
        // they are collected here to reduce clutter in console.log and forEach
        this.$$conf = {
          promise: readyPromise,
          inst: $firebase,
          binding: new ThreeWayBinding(this),
          destroyFn: destroyFn,
          listeners: []
        };

        // this bit of magic makes $$conf non-enumerable and non-configurable
        // and non-writable (its properties are still writable but the ref cannot be replaced)
        // we declare it above so the IDE can relax
        Object.defineProperty(this, '$$conf', {
          value: this.$$conf
        });

        this.$id = $firebaseUtils.getKey($firebase.$ref().ref());
        this.$priority = null;

        $firebaseUtils.applyDefaults(this, this.$$defaults);
      }

      FirebaseObject.prototype = {
        /**
         * Saves all data on the FirebaseObject back to Firebase.
         * @returns a promise which will resolve after the save is completed.
         */
        $save: function () {
          var self = this;
          return self.$inst().$set($firebaseUtils.toJSON(self))
            .then(function(ref) {
              self.$$notify();
              return ref;
            });
        },

        /**
         * Removes all keys from the FirebaseObject and also removes
         * the remote data from the server.
         *
         * @returns a promise which will resolve after the op completes
         */
        $remove: function() {
          var self = this;
          $firebaseUtils.trimKeys(this, {});
          this.$value = null;
          return self.$inst().$remove().then(function(ref) {
            self.$$notify();
            return ref;
          });
        },

        /**
         * The loaded method is invoked after the initial batch of data arrives from the server.
         * When this resolves, all data which existed prior to calling $asObject() is now cached
         * locally in the object.
         *
         * As a shortcut is also possible to pass resolve/reject methods directly into this
         * method just as they would be passed to .then()
         *
         * @param {Function} resolve
         * @param {Function} reject
         * @returns a promise which resolves after initial data is downloaded from Firebase
         */
        $loaded: function(resolve, reject) {
          var promise = this.$$conf.promise;
          if (arguments.length) {
            // allow this method to be called just like .then
            // by passing any arguments on to .then
            promise = promise.then.call(promise, resolve, reject);
          }
          return promise;
        },

        /**
         * @returns the original $firebase object used to create this object.
         */
        $inst: function () {
          return this.$$conf.inst;
        },

        /**
         * Creates a 3-way data sync between this object, the Firebase server, and a
         * scope variable. This means that any changes made to the scope variable are
         * pushed to Firebase, and vice versa.
         *
         * If scope emits a $destroy event, the binding is automatically severed. Otherwise,
         * it is possible to unbind the scope variable by using the `unbind` function
         * passed into the resolve method.
         *
         * Can only be bound to one scope variable at a time. If a second is attempted,
         * the promise will be rejected with an error.
         *
         * @param {object} scope
         * @param {string} varName
         * @returns a promise which resolves to an unbind method after data is set in scope
         */
        $bindTo: function (scope, varName) {
          var self = this;
          return self.$loaded().then(function () {
            return self.$$conf.binding.bindTo(scope, varName);
          });
        },

        /**
         * Listeners passed into this method are notified whenever a new change is received
         * from the server. Each invocation is sent an object containing
         * { type: 'updated', key: 'my_firebase_id' }
         *
         * This method returns an unbind function that can be used to detach the listener.
         *
         * @param {Function} cb
         * @param {Object} [context]
         * @returns {Function} invoke to stop observing events
         */
        $watch: function (cb, context) {
          var list = this.$$conf.listeners;
          list.push([cb, context]);
          // an off function for cancelling the listener
          return function () {
            var i = list.findIndex(function (parts) {
              return parts[0] === cb && parts[1] === context;
            });
            if (i > -1) {
              list.splice(i, 1);
            }
          };
        },

        /**
         * Informs $firebase to stop sending events and clears memory being used
         * by this object (delete's its local content).
         */
        $destroy: function (err) {
          var self = this;
          if (!self.$isDestroyed) {
            self.$isDestroyed = true;
            self.$$conf.binding.destroy();
            $firebaseUtils.each(self, function (v, k) {
              delete self[k];
            });
            self.$$conf.destroyFn(err);
          }
        },

        /**
         * Called by $firebase whenever an item is changed at the server.
         * This method must exist on any objectFactory passed into $firebase.
         *
         * It should return true if any changes were made, otherwise `$$notify` will
         * not be invoked.
         *
         * @param {object} snap a Firebase snapshot
         * @return {boolean} true if any changes were made.
         */
        $$updated: function (snap) {
          // applies new data to this object
          var changed = $firebaseUtils.updateRec(this, snap);
          // applies any defaults set using $$defaults
          $firebaseUtils.applyDefaults(this, this.$$defaults);
          // returning true here causes $$notify to be triggered
          return changed;
        },

        /**
         * Called whenever a security error or other problem causes the listeners to become
         * invalid. This is generally an unrecoverable error.
         * @param {Object} err which will have a `code` property and possibly a `message`
         */
        $$error: function (err) {
          // prints an error to the console (via Angular's logger)
          $log.error(err);
          // frees memory and cancels any remaining listeners
          this.$destroy(err);
        },

        /**
         * Called internally by $bindTo when data is changed in $scope.
         * Should apply updates to this record but should not call
         * notify().
         */
        $$scopeUpdated: function(newData) {
          // we use a one-directional loop to avoid feedback with 3-way bindings
          // since set() is applied locally anyway, this is still performant
          return this.$inst().$set($firebaseUtils.toJSON(newData));
        },

        /**
         * Updates any bound scope variables and
         * notifies listeners registered with $watch
         */
        $$notify: function() {
          var self = this, list = this.$$conf.listeners.slice();
          // be sure to do this after setting up data and init state
          angular.forEach(list, function (parts) {
            parts[0].call(parts[1], {event: 'value', key: self.$id});
          });
        },

        /**
         * Overrides how Angular.forEach iterates records on this object so that only
         * fields stored in Firebase are part of the iteration. To include meta fields like
         * $id and $priority in the iteration, utilize for(key in obj) instead.
         */
        forEach: function(iterator, context) {
          return $firebaseUtils.each(this, iterator, context);
        }
      };

      /**
       * This method allows FirebaseObject to be copied into a new factory. Methods passed into this
       * function will be added onto the object's prototype. They can override existing methods as
       * well.
       *
       * In addition to passing additional methods, it is also possible to pass in a class function.
       * The prototype on that class function will be preserved, and it will inherit from
       * FirebaseObject. It's also possible to do both, passing a class to inherit and additional
       * methods to add onto the prototype.
       *
       * Once a factory is obtained by this method, it can be passed into $firebase as the
       * `objectFactory` parameter:
       *
       *

       * var MyFactory = $FirebaseObject.$extendFactory({
       *    // add a method onto the prototype that prints a greeting
       *    getGreeting: function() {
       *       return 'Hello ' + this.first_name + ' ' + this.last_name + '!';
       *    }
       * });
       *
       * // use our new factory in place of $FirebaseObject
       * var obj = $firebase(ref, {objectFactory: MyFactory}).$asObject();
       *

       *
       * @param {Function} [ChildClass] a child class which should inherit FirebaseObject
       * @param {Object} [methods] a list of functions to add onto the prototype
       * @returns {Function} a new factory suitable for use with $firebase
       */
      FirebaseObject.$extendFactory = function(ChildClass, methods) {
        if( arguments.length === 1 && angular.isObject(ChildClass) ) {
          methods = ChildClass;
          ChildClass = function() { FirebaseObject.apply(this, arguments); };
        }
        return $firebaseUtils.inherit(ChildClass, FirebaseObject, methods);
      };

      /**
       * Creates a three-way data binding on a scope variable.
       *
       * @param {FirebaseObject} rec
       * @returns {*}
       * @constructor
       */
      function ThreeWayBinding(rec) {
        this.subs = [];
        this.scope = null;
        this.key = null;
        this.rec = rec;
      }

      ThreeWayBinding.prototype = {
        assertNotBound: function(varName) {
          if( this.scope ) {
            var msg = 'Cannot bind to ' + varName + ' because this instance is already bound to ' +
              this.key + '; one binding per instance ' +
              '(call unbind method or create another $firebase instance)';
            $log.error(msg);
            return $firebaseUtils.reject(msg);
          }
        },

        bindTo: function(scope, varName) {
          function _bind(self) {
            var sending = false;
            var parsed = $parse(varName);
            var rec = self.rec;
            self.scope = scope;
            self.varName = varName;

            function equals(rec) {
              var parsed = getScope();
              var newData = $firebaseUtils.scopeData(rec);
              return angular.equals(parsed, newData) &&
                parsed.$priority === rec.$priority &&
                parsed.$value === rec.$value;
            }

            function getScope() {
              return $firebaseUtils.scopeData(parsed(scope));
            }

            function setScope(rec) {
              parsed.assign(scope, $firebaseUtils.scopeData(rec));
            }

            var send = $firebaseUtils.debounce(function() {
              var scopeData = getScope();
              rec.$$scopeUpdated(scopeData)
                  ['finally'](function() {
                    sending = false;
                    if(!scopeData.hasOwnProperty('$value')){
                      delete rec.$value;
                      delete parsed(scope).$value;
                    }
                  }
              );
            }, 50, 500);

            var scopeUpdated = function() {
              if( !equals(rec) ) {
                sending = true;
                send();
              }
            };

            var recUpdated = function() {
              if( !sending && !equals(rec) ) {
                setScope(rec);
              }
            };

            // $watch will not check any vars prefixed with $, so we
            // manually check $priority and $value using this method
            function checkMetaVars() {
              var dat = parsed(scope);
              if( dat.$value !== rec.$value || dat.$priority !== rec.$priority ) {
                scopeUpdated();
              }
            }

            self.subs.push(scope.$watch(checkMetaVars));

            setScope(rec);
            self.subs.push(scope.$on('$destroy', self.unbind.bind(self)));

            // monitor scope for any changes
            self.subs.push(scope.$watch(varName, scopeUpdated, true));

            // monitor the object for changes
            self.subs.push(rec.$watch(recUpdated));

            return self.unbind.bind(self);
          }

          return this.assertNotBound(varName) || _bind(this);
        },

        unbind: function() {
          if( this.scope ) {
            angular.forEach(this.subs, function(unbind) {
              unbind();
            });
            this.subs = [];
            this.scope = null;
            this.key = null;
          }
        },

        destroy: function() {
          this.unbind();
          this.rec = null;
        }
      };

      return FirebaseObject;
    }
  ]);
})();
<|endoftext|>


<commit_before>'use strict';

var defineProperty = require('./properties.js').defineProperty;

var crypto = global.crypto || global.msCrypto;
if (!crypto || !crypto.getRandomValues) {
    console.log('WARNING: Missing strong random number source; using weak randomBytes');
    crypto = {
        getRandomValues: function(buffer) {
            for (var round = 0; round < 20; round++) {
                for (var i = 0; i < buffer.length; i++) {
                    if (round) {
                        buffer[i] ^= parseInt(256 * Math.random());
                    } else {
                        buffer[i] = parseInt(256 * Math.random());
                    }
                }
            }

            return buffer;
        },
        _weakCrypto: true
    };
} else {
    console.log('Found strong random number source');
}

function randomBytes(length) {
    if (length <= 0 || length > 1024 || parseInt(length) != length) {
        throw new Error('invalid length');
    }

    var result = new Uint8Array(length);
    crypto.getRandomValues(result);
    return result;
};

if (crypto._weakCrypto === true) {
    defineProperty(randomBytes, '_weakCrypto', true);
}

module.exports = randomBytes;
<commit_msg>Removed chatty library (See issue #36).<commit_after>'use strict';

var defineProperty = require('./properties.js').defineProperty;

var crypto = global.crypto || global.msCrypto;
if (!crypto || !crypto.getRandomValues) {

    console.log('WARNING: Missing strong random number source; using weak randomBytes');

    crypto = {
        getRandomValues: function(buffer) {
            for (var round = 0; round < 20; round++) {
                for (var i = 0; i < buffer.length; i++) {
                    if (round) {
                        buffer[i] ^= parseInt(256 * Math.random());
                    } else {
                        buffer[i] = parseInt(256 * Math.random());
                    }
                }
            }

            return buffer;
        },
        _weakCrypto: true
    };
}

function randomBytes(length) {
    if (length <= 0 || length > 1024 || parseInt(length) != length) {
        throw new Error('invalid length');
    }

    var result = new Uint8Array(length);
    crypto.getRandomValues(result);
    return result;
};

if (crypto._weakCrypto === true) {
    defineProperty(randomBytes, '_weakCrypto', true);
}

module.exports = randomBytes;
<|endoftext|>


<commit_before>(function() {

    var step = 1,
        d = document,
        w = window;
    var json = {},
        jsonCtr = 0,
        videoId = "";
    //YoutubeのURLからIDだけ抜き出す正規表現
    var youtubeId = /youtu(.be\/|be.com\/.*v=)([\d\w-]+).*/;
    var u = location.href;
    // 親JSからパラメータを取得
    var script = d.getElementById("bmlt");
    var out = script.out,
        scs = script.scs,
        provider = "",
        //YouTube用データ配列
        data = {"movieWidth":"","movieHtml":"","movieThumbnail":""};
        fmt = decodeURIComponent(script.fmt);

    // bookmarkletの予約語（2個）
    var bmAry = ['html', 'thumbnail'];

    // メイン処理（非同期実行を防ぐ為にTimerを利用）
    var timerId = setInterval(function() {
            try {
                switch (step) {
                    case 1:
                        step = 0;
                        checkURL();
                        break;
                    case 2:
                        step = 0;
                        createEndpoint();
                        break;
                    case 3:
                        step = 0;
                        getData();
                        break;
                    case 4:
                        step = 0;
                        dispData();
                        break;
                    case 5:
                        cleanup();
                        return 0;
                }
            } catch (e) {
                alert("Something may be wrong.\n->" + e);
                cleanup();
            }
        },
        100);

        function cleanup() {
            while (d.getElementById("bmlt"))
                d.getElementById("bmlt").parentNode.removeChild(d.getElementById("bmlt"));
            clearInterval(timerId);
            timerId = null;
        }

    function checkURL() {
        switch (true) {
            case /youtu(.be|be.com)/.test(u):
            case /vimeo/.test(u):
            case /slideshare/.test(u):
            case /dailymotion/.test(u):
            case /insta(gram.com|gr.am)/.test(u):
            case /twitter/.test(u):
            case /flic(kr.com|.kr)/.test(u):
                step = 2;
                break;
            default:
                u = prompt("Input Media URL", "");
                step = 2;
                break;
        }
    }

    function createEndpoint() {
        switch (true) {
            case /youtu(.be|be.com)/.test(u):
                var result = u.match(youtubeId);
                videoId = result[2];
                provider = "YouTube";
                step = 3;
                break;
            case /vimeo/.test(u):
                endpointURL = "http://vimeo.com/api/oembed.json?url=" + u + "&width=" + scs;
                provider = "Vimeo";
                step = 3;
                break;
            case /slideshare/.test(u):
                endpointURL = "http://www.slideshare.net/api/oembed/2?url=" + u + "&format=jsonp&maxwidth=" + scs;
                provider = "SlideShare";
                step = 3;
                break;
            case /dailymotion/.test(u):
                if (u.match("touch")) {
                    u = u.replace("touch", "www");
                }
                endpointURL = "http://www.dailymotion.com/services/oembed?format=json&url=" + u + "&maxwidth=" + scs;
                provider = "Dailymotion";
                step = 3;
                break;
            case /insta(gram.com|gr.am)/.test(u):
                endpointURL = "http://api.instagram.com/oembed?url=" + u + "&maxwidth=" + scs + "&format=json";
                provider = "Instagram";
                step = 3;
                break;
            case /twitter/.test(u):
                endpointURL = "https://api.twitter.com/1/statuses/oembed.json?url=" + u + "&maxwidth=" + scs + "&format=json";
                provider = "Twitter";
                step = 3;
                break;
            default:
                alert("Not Found ...");
                step = 5;
                return;
        }
    }

    //メディア取得
    function getData() {
        //YouTube以外の場合
        if (provider != "YouTube") {
            var s = d.createElement("script");
            var src = endpointURL;
            while (d.getElementById("getData")) {
                d.getElementById("getData").parentNode.removeChild(d.getElementById("getData"));
            }
            s.charset = "utf-8";
            s.src = src + "&callback=result";
            s.id = "getData";
            d.body.appendChild(s);
            w.result = function(data) {
                movieWidth = data.width;
                movieHeight = data.height;
                movieHtml = data.html;
                //InstagramとSlideShareはテンプレートにサムネイルがあっても出力しない。
                if (provider == "Instagram" || "SlideShare" || "Twitter") {
                    movieThumbnail = "";
                } else {
                    movieThumbnail = '';
                }
                jsonCtr = 1;
                for (var i = 0; i < jsonCtr; i++) {
                    json[i] = data[i];
                }
                step = 4;
            }
            //YouTubeの場合
        } else {
            width = scs;
            height = (9 / 16) * width;
            height = Math.round(height);
            thumbnail_url = '';
            html = '';
            movieWidth = width;
            movieHtml = html;
            movieThumbnail = thumbnail_url;
            jsonCtr = 1;
            for (var i = 0; i < jsonCtr; i++) {
                json[i] = data[i];
            }
            step = 4;
        }
    }

    // 結果の整理と出力方法ごとの処理
    function dispData() {
        var x = '',
            chk = '';
        for (var i = 0; i < jsonCtr; i++) {
            var z = json[i],
                pData = fmt;

            // 結果をbookmarklet予約語に変換してfmtを置換
            var bmData = handData(z);
            for (var j = 0; j < bmAry.length; j++) {
                var key = bmAry[j],
                    value = bmData[key],
                    reg = new RegExp('\\${' + key + '}', 'g');
                pData = pData.replace(reg, value);
            }
            x = x + pData + '\n\n';
            chk = chk + pData;
        }

        if (chk != '') {
            // 出力方法ごとの処理（プレビュー表示）
            if (out == "preview") {
                d.body.innerHTML =
                    '
' +
                    "<form><input type='button' value='プレビュー表示を消す' onclick='javascript:" +
                    'var a=document.getElementById("bkmlt_preview");a.parentNode.removeChild(a);' +
                    "'>　<input type='button' value='HTMLを選択する' onclick='javascript:" +
                    'var a=document.getElementById("bkmklt_ret");a.focus();' +
                    "'>　<input type='button' value='HTMLの内容でプレビューを書き直す' onclick='javascript:" +
                    'var a=document.getElementById("bkmklt_ret").value;' +
                    'document.getElementById("bkmklt_rewrite").innerHTML=a;' +
                    "'></form>" + '<textarea style="width:99%;font-size:80%;" rows="10" id="bkmklt_ret"' +
                    'onfocus="javascript:this.select();">' + x + '</textarea>

' +
                    x + '

' + d.body.innerHTML;
            }
            // 出力方法ごとの処理（ポップアップ表示）
            if (out == "popup") {
                prompt("result", x);
            }
            // 出力方法ごとの処理（ポップアップ→Textforce連携）
            if (out == "pop-textforce") {
                prompt("result", x);
                w.location = 'textforce://';
            }
            // 出力方法ごとの処理（Texeforce連携）
            if (out == "textforce") {
                w.location = 'textforce://file?path=/blog.html&method=write&after=quick_look&text=' + encodeURIComponent(x);
            }
            // 出力方法ごとの処理（Texeforce連携しSafariに戻る）
            if (out == "safari-textforce") {
                w.location = 'textforce://file?path=/blog.html&method=write&after=quick_look&text=' + encodeURIComponent(x) + '&callback=' + encodeURIComponent(location.href);
            }
            // 出力方法ごとの処理（DraftPad連携）
            if (out == "draftpad") {
                w.location = 'draftpad:///insert?after=' + encodeURIComponent(x);
            }
            // 出力方法ごとの処理（するぷろ連携）
            if (out == "slpro") {
                w.location = 'slpro://' + encodeURIComponent(x);
            }
            // 出力方法ごとの処理（Moblogger連携）
            if (out == "moblogger") {
                prompt("result", x);
                w.location = 'moblogger://';
            }
            // 出力方法ごとの処理（Mobloggerを起動して追記）
            if (out == "moblogger-app") {
                w.location = 'moblogger://append?text=' + encodeURIComponent(x);
            }
            // 出力方法ごとの処理（Mobloggerを起動してクリップボードにコピー）
            if (out == "moblogger-pb") {
                w.location = 'moblogger://pboard?text=' + encodeURIComponent(x);
            }
            // 出力方法ごとの処理（MyEditor連携）
            if (out == "myeditor") {
                prompt("result", x);
                w.location = 'myeditor://';
            }
            // 出力方法ごとの処理（MyEditorを起動してカーソル位置にコピー）
            if (out == "myeditor-cursor") {
                w.location = 'myeditor://cursor?text=' + encodeURIComponent(x);
            }
            // 出力方法ごとの処理（Rowlineを起動して文末に追加）
            if (out == "rowline") {
                w.location = 'rowline:///set?loc=bottom&view=lines&callback=seeq://&text=' + encodeURIComponent(x);
            }
            // 出力方法ごとの処理（@matubizさん作MyScripts用スクリプト、TextHandlerに送信）
            if (out == "msth") {
                w.location = 'myscripts://run?title=TextHandler&text=' + encodeURIComponent(x);
            }
            // 出力方法ごとの処理（ThumbEditに送る）
            if (out == "thumbedit") {
                w.location = 'thumbedit://?text=' + encodeURIComponent(x);
            }
            // 出力方法ごとの処理（ThumbEditに追記）
            if (out == "thumbedit-insert") {
                w.location = 'thumbedit://?text=' + encodeURIComponent(x) + '&mode=insert';
            }
            // 出力方法ごとの処理（PressSync Proに送る）
            if (out == "presssync") {
                w.location = 'presssync:///message?' + encodeURIComponent(x);
            }
            // 出力方法ごとの処理（PressSync Proに送る）
            if (out == "textwell") {
                w.location = 'textwell:///insert?text=' + encodeURIComponent(x);
            }
            // 出力方法ごとの処理（はてなブログで新規作成）
            if (out == "hatenablog") {
                w.location = 'hatenablog:///new?title=new%20post&body=' + encodeURIComponent(x);
            }
            // 出力方法ごとの処理（Draftsで新規作成）
            if (out == "drafts") {
                w.location = 'drafts://x-callback-url/create?text=' + encodeURIComponent(x);
            }
            // 出力方法ごとの処理（Drafts 4で新規作成）
            if (out == "drafts4") {
                w.location = 'drafts4://x-callback-url/create?text=' + encodeURIComponent(x);
            }
            // 出力方法ごとの処理（AmeEditorに送る）
            if (out == "ameeditor") {
                w.location = 'ameeditor://insert?text=' + encodeURIComponent(x);
            } // 出力方法ごとの処理（SLPRO Xに送る）
            if (out == "slpro-x") {
                w.location = 'slpro-x://?q=' + encodeURIComponent(x);
            }
        }
        step = 5;
    }

    // Bookmarklet予約語へのセット

    function handData(data) {
        var x = new Array(bmAry);
        x.html = movieHtml;
        x.width = movieWidth;
        x.thumbnail = movieThumbnail;
        return x;
    }

    // 親JSからGET形式でパラメータを引継ぐ為の関数

    function getJs(searchKey) {
        var scripts = document.getElementsByTagName("script"),
            urlArg, params = {};
        for (var i = 0; i < scripts.length; i++) {
            var tmp = scripts.item(i);
            if (tmp.src.indexOf(bmBase) != -1) {
                urlArg = tmp.src.slice(bmBase.length + 1);
                break;
            }
        }
        var paramAry, jsonKey, jsonVal, pos;
        if (urlArg) paramAry = urlArg.split("&");
        if (paramAry) {
            for (var i = 0; i < paramAry.length; i++) {
                var pos = paramAry[i].indexOf('=');
                if (pos > 0) {
                    jsonKey = paramAry[i].substring(0, pos);
                    jsonVal = paramAry[i].substring(pos + 1);
                }
                if (jsonKey == searchKey) return jsonVal;
            }
        }
        return null;
    }

    // 前詰めゼロ処理

    function zeroFormat(num, max) {
        var tmp = "" + num;
        while (tmp.length < max) {
            tmp = "0" + tmp;
        }
        return tmp;
    }

    // utimeの数値を+9時間して日付文字に整形

    function strDatetime(num) {
        var dt = new Date(),
            tmp;
        dt.setTime(num + '000');
        dt.setHours(dt.getHours() + 9);
        tmp = zeroFormat(dt.getFullYear(), 4) + '/' + zeroFormat(dt.getMonth(), 2) + '/' + zeroFormat(dt.getDate(), 2) + ' ' + zeroFormat(dt.getHours(), 2) + ':' + zeroFormat(dt.getMinutes(), 2);
        return tmp;
    }
})();
<commit_msg>サムネイル出力の修正<commit_after>(function() {

    var step = 1,
        d = document,
        w = window;
    var json = {},
        jsonCtr = 0,
        videoId = "";
    //YoutubeのURLからIDだけ抜き出す正規表現
    var youtubeId = /youtu(.be\/|be.com\/.*v=)([\d\w-]+).*/;
    var u = location.href;
    // 親JSからパラメータを取得
    var script = d.getElementById("bmlt");
    var out = script.out,
        scs = script.scs,
        provider = "",
        //YouTube用データ配列
        data = {"movieWidth":"","movieHtml":"","movieThumbnail":""};
        fmt = decodeURIComponent(script.fmt);

    // bookmarkletの予約語（2個）
    var bmAry = ['html', 'thumbnail'];

    // メイン処理（非同期実行を防ぐ為にTimerを利用）
    var timerId = setInterval(function() {
            try {
                switch (step) {
                    case 1:
                        step = 0;
                        checkURL();
                        break;
                    case 2:
                        step = 0;
                        createEndpoint();
                        break;
                    case 3:
                        step = 0;
                        getData();
                        break;
                    case 4:
                        step = 0;
                        dispData();
                        break;
                    case 5:
                        cleanup();
                        return 0;
                }
            } catch (e) {
                alert("Something may be wrong.\n->" + e);
                cleanup();
            }
        },
        100);

        function cleanup() {
            while (d.getElementById("bmlt"))
                d.getElementById("bmlt").parentNode.removeChild(d.getElementById("bmlt"));
            clearInterval(timerId);
            timerId = null;
        }

    function checkURL() {
        switch (true) {
            case /youtu(.be|be.com)/.test(u):
            case /vimeo/.test(u):
            case /slideshare/.test(u):
            case /dailymotion/.test(u):
            case /insta(gram.com|gr.am)/.test(u):
            case /twitter/.test(u):
            case /flic(kr.com|.kr)/.test(u):
                step = 2;
                break;
            default:
                u = prompt("Input Media URL", "");
                step = 2;
                break;
        }
    }

    function createEndpoint() {
        switch (true) {
            case /youtu(.be|be.com)/.test(u):
                var result = u.match(youtubeId);
                videoId = result[2];
                provider = "YouTube";
                step = 3;
                break;
            case /vimeo/.test(u):
                endpointURL = "http://vimeo.com/api/oembed.json?url=" + u + "&width=" + scs;
                provider = "Vimeo";
                step = 3;
                break;
            case /slideshare/.test(u):
                endpointURL = "http://www.slideshare.net/api/oembed/2?url=" + u + "&format=jsonp&maxwidth=" + scs;
                provider = "SlideShare";
                step = 3;
                break;
            case /dailymotion/.test(u):
                if (u.match("touch")) {
                    u = u.replace("touch", "www");
                }
                endpointURL = "http://www.dailymotion.com/services/oembed?format=json&url=" + u + "&maxwidth=" + scs;
                provider = "Dailymotion";
                step = 3;
                break;
            case /insta(gram.com|gr.am)/.test(u):
                endpointURL = "http://api.instagram.com/oembed?url=" + u + "&maxwidth=" + scs + "&format=json";
                provider = "Instagram";
                step = 3;
                break;
            case /twitter/.test(u):
                endpointURL = "https://api.twitter.com/1/statuses/oembed.json?url=" + u + "&maxwidth=" + scs + "&format=json";
                provider = "Twitter";
                step = 3;
                break;
            default:
                alert("Not Found ...");
                step = 5;
                return;
        }
    }

    //メディア取得
    function getData() {
        //YouTube以外の場合
        if (provider != "YouTube") {
            var s = d.createElement("script");
            var src = endpointURL;
            while (d.getElementById("getData")) {
                d.getElementById("getData").parentNode.removeChild(d.getElementById("getData"));
            }
            s.charset = "utf-8";
            s.src = src + "&callback=result";
            s.id = "getData";
            d.body.appendChild(s);
            w.result = function(data) {
                movieWidth = data.width;
                movieHeight = data.height;
                movieHtml = data.html;
                //InstagramとSlideShareはテンプレートにサムネイルがあっても出力しない。
                if (provider == "Instagram" || provider == "SlideShare" || provider == "Twitter") {
                    movieThumbnail = "";
                } else {
                    movieThumbnail = '';
                }
                jsonCtr = 1;
                for (var i = 0; i < jsonCtr; i++) {
                    json[i] = data[i];
                }
                step = 4;
            }
            //YouTubeの場合
        } else {
            width = scs;
            height = (9 / 16) * width;
            height = Math.round(height);
            thumbnail_url = '';
            html = '';
            movieWidth = width;
            movieHtml = html;
            movieThumbnail = thumbnail_url;
            jsonCtr = 1;
            for (var i = 0; i < jsonCtr; i++) {
                json[i] = data[i];
            }
            step = 4;
        }
    }

    // 結果の整理と出力方法ごとの処理
    function dispData() {
        var x = '',
            chk = '';
        for (var i = 0; i < jsonCtr; i++) {
            var z = json[i],
                pData = fmt;

            // 結果をbookmarklet予約語に変換してfmtを置換
            var bmData = handData(z);
            for (var j = 0; j < bmAry.length; j++) {
                var key = bmAry[j],
                    value = bmData[key],
                    reg = new RegExp('\\${' + key + '}', 'g');
                pData = pData.replace(reg, value);
            }
            x = x + pData + '\n\n';
            chk = chk + pData;
        }

        if (chk != '') {
            // 出力方法ごとの処理（プレビュー表示）
            if (out == "preview") {
                d.body.innerHTML =
                    '
' +
                    "<form><input type='button' value='プレビュー表示を消す' onclick='javascript:" +
                    'var a=document.getElementById("bkmlt_preview");a.parentNode.removeChild(a);' +
                    "'>　<input type='button' value='HTMLを選択する' onclick='javascript:" +
                    'var a=document.getElementById("bkmklt_ret");a.focus();' +
                    "'>　<input type='button' value='HTMLの内容でプレビューを書き直す' onclick='javascript:" +
                    'var a=document.getElementById("bkmklt_ret").value;' +
                    'document.getElementById("bkmklt_rewrite").innerHTML=a;' +
                    "'></form>" + '<textarea style="width:99%;font-size:80%;" rows="10" id="bkmklt_ret"' +
                    'onfocus="javascript:this.select();">' + x + '</textarea>

' +
                    x + '

' + d.body.innerHTML;
            }
            // 出力方法ごとの処理（ポップアップ表示）
            if (out == "popup") {
                prompt("result", x);
            }
            // 出力方法ごとの処理（ポップアップ→Textforce連携）
            if (out == "pop-textforce") {
                prompt("result", x);
                w.location = 'textforce://';
            }
            // 出力方法ごとの処理（Texeforce連携）
            if (out == "textforce") {
                w.location = 'textforce://file?path=/blog.html&method=write&after=quick_look&text=' + encodeURIComponent(x);
            }
            // 出力方法ごとの処理（Texeforce連携しSafariに戻る）
            if (out == "safari-textforce") {
                w.location = 'textforce://file?path=/blog.html&method=write&after=quick_look&text=' + encodeURIComponent(x) + '&callback=' + encodeURIComponent(location.href);
            }
            // 出力方法ごとの処理（DraftPad連携）
            if (out == "draftpad") {
                w.location = 'draftpad:///insert?after=' + encodeURIComponent(x);
            }
            // 出力方法ごとの処理（するぷろ連携）
            if (out == "slpro") {
                w.location = 'slpro://' + encodeURIComponent(x);
            }
            // 出力方法ごとの処理（Moblogger連携）
            if (out == "moblogger") {
                prompt("result", x);
                w.location = 'moblogger://';
            }
            // 出力方法ごとの処理（Mobloggerを起動して追記）
            if (out == "moblogger-app") {
                w.location = 'moblogger://append?text=' + encodeURIComponent(x);
            }
            // 出力方法ごとの処理（Mobloggerを起動してクリップボードにコピー）
            if (out == "moblogger-pb") {
                w.location = 'moblogger://pboard?text=' + encodeURIComponent(x);
            }
            // 出力方法ごとの処理（MyEditor連携）
            if (out == "myeditor") {
                prompt("result", x);
                w.location = 'myeditor://';
            }
            // 出力方法ごとの処理（MyEditorを起動してカーソル位置にコピー）
            if (out == "myeditor-cursor") {
                w.location = 'myeditor://cursor?text=' + encodeURIComponent(x);
            }
            // 出力方法ごとの処理（Rowlineを起動して文末に追加）
            if (out == "rowline") {
                w.location = 'rowline:///set?loc=bottom&view=lines&callback=seeq://&text=' + encodeURIComponent(x);
            }
            // 出力方法ごとの処理（@matubizさん作MyScripts用スクリプト、TextHandlerに送信）
            if (out == "msth") {
                w.location = 'myscripts://run?title=TextHandler&text=' + encodeURIComponent(x);
            }
            // 出力方法ごとの処理（ThumbEditに送る）
            if (out == "thumbedit") {
                w.location = 'thumbedit://?text=' + encodeURIComponent(x);
            }
            // 出力方法ごとの処理（ThumbEditに追記）
            if (out == "thumbedit-insert") {
                w.location = 'thumbedit://?text=' + encodeURIComponent(x) + '&mode=insert';
            }
            // 出力方法ごとの処理（PressSync Proに送る）
            if (out == "presssync") {
                w.location = 'presssync:///message?' + encodeURIComponent(x);
            }
            // 出力方法ごとの処理（PressSync Proに送る）
            if (out == "textwell") {
                w.location = 'textwell:///insert?text=' + encodeURIComponent(x);
            }
            // 出力方法ごとの処理（はてなブログで新規作成）
            if (out == "hatenablog") {
                w.location = 'hatenablog:///new?title=new%20post&body=' + encodeURIComponent(x);
            }
            // 出力方法ごとの処理（Draftsで新規作成）
            if (out == "drafts") {
                w.location = 'drafts://x-callback-url/create?text=' + encodeURIComponent(x);
            }
            // 出力方法ごとの処理（Drafts 4で新規作成）
            if (out == "drafts4") {
                w.location = 'drafts4://x-callback-url/create?text=' + encodeURIComponent(x);
            }
            // 出力方法ごとの処理（AmeEditorに送る）
            if (out == "ameeditor") {
                w.location = 'ameeditor://insert?text=' + encodeURIComponent(x);
            } // 出力方法ごとの処理（SLPRO Xに送る）
            if (out == "slpro-x") {
                w.location = 'slpro-x://?q=' + encodeURIComponent(x);
            }
        }
        step = 5;
    }

    // Bookmarklet予約語へのセット

    function handData(data) {
        var x = new Array(bmAry);
        x.html = movieHtml;
        x.width = movieWidth;
        x.thumbnail = movieThumbnail;
        return x;
    }

    // 親JSからGET形式でパラメータを引継ぐ為の関数

    function getJs(searchKey) {
        var scripts = document.getElementsByTagName("script"),
            urlArg, params = {};
        for (var i = 0; i < scripts.length; i++) {
            var tmp = scripts.item(i);
            if (tmp.src.indexOf(bmBase) != -1) {
                urlArg = tmp.src.slice(bmBase.length + 1);
                break;
            }
        }
        var paramAry, jsonKey, jsonVal, pos;
        if (urlArg) paramAry = urlArg.split("&");
        if (paramAry) {
            for (var i = 0; i < paramAry.length; i++) {
                var pos = paramAry[i].indexOf('=');
                if (pos > 0) {
                    jsonKey = paramAry[i].substring(0, pos);
                    jsonVal = paramAry[i].substring(pos + 1);
                }
                if (jsonKey == searchKey) return jsonVal;
            }
        }
        return null;
    }

    // 前詰めゼロ処理

    function zeroFormat(num, max) {
        var tmp = "" + num;
        while (tmp.length < max) {
            tmp = "0" + tmp;
        }
        return tmp;
    }

    // utimeの数値を+9時間して日付文字に整形

    function strDatetime(num) {
        var dt = new Date(),
            tmp;
        dt.setTime(num + '000');
        dt.setHours(dt.getHours() + 9);
        tmp = zeroFormat(dt.getFullYear(), 4) + '/' + zeroFormat(dt.getMonth(), 2) + '/' + zeroFormat(dt.getDate(), 2) + ' ' + zeroFormat(dt.getHours(), 2) + ':' + zeroFormat(dt.getMinutes(), 2);
        return tmp;
    }
})();
<|endoftext|>