<commit_before>export { decoder };

const base64String = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

// Mapping table for base64 (as Uint8) to index.
let table_base64_Uint8_to_index = new Array(256); // Faster than using Uint8Array().
{
  // For all non-base64 codes, using value greater than 63 (i.e. impossible base64) for identifying them.
  for (let i = 0; i < table_base64_Uint8_to_index.length; ++i)
    table_base64_Uint8_to_index[ i ] = 255;

  // For all legal base64 codes, using value between [0, 63].
  {
    for (let i = 0; i < base64String.length; ++i) {
      let codePoint = base64String.codePointAt(i);
      table_base64_Uint8_to_index[ codePoint ] = i;
    }

    // Support decoding URL-safe base64 strings, as Node.js does.
    // See: https://en.wikipedia.org/wiki/Base64#URL_applications
    table_base64_Uint8_to_index['-'.charCodeAt(0)] = 62;
    table_base64_Uint8_to_index['_'.charCodeAt(0)] = 63;
  }
}

/**
 *
 * @param {ArrayBuffer} sourceBase64ArrayBuffer
 *   The input base64 data as ArrayBuffer. If the last bytes not enough 4 bytes, they will be discarded (will
 * not be decoded). If an input byte is not a legal base64 code (i.e. not A..Z, a..z, 0..9, +, /, -, _), the
 * byte will be skipped (as if it does not exist). So the input bytes can be separated by new line.
 *
 * @param {Uint32} skipLineCount
 *   Skip how many lines in the source before decoding.
 *
 * @param {ValueMax.Percentage.Aggregate} progressToYield
 *   Return this when every time yield.
 *
 * @param {ValueMax.Percentage.Concrete}  progressToAdvance
 *   Increase this when every time advanced.
 *
 * @param {Uint32} suspendByteCount
 *   Everytime so many bytes decoded, yield for releasing CPU time (and reporting progress).
 *   Default is 1024 bytes.
 *
 * @yield {ValueMax.Percentage.Aggregate or Uint8Array}
 *   Yield ( value = progressYield ) when ( done = false ).
 *   Yield ( value = decoded data as Uint8Array ) when ( done = true ).
 */
function* decoder(
  sourceBase64ArrayBuffer, skipLineCount, progressToYield, progressToAdvance, suspendByteCount) {

  // 0. Initialize.

  // If undefined or null or negative or zero or less than 1, set to default.
  // Note: Bitwising OR with zero is for converting to integer (if it is undefined or null).
  if ((suspendByteCount | 0) <= 0)
    suspendByteCount = 1024;

  let sourceByteLength = sourceBase64ArrayBuffer.byteLength;
  let sourceBytes = new Uint8Array( sourceBase64ArrayBuffer );

  // Initialize progress.
  progressToAdvance.accumulation = 0;
  progressToAdvance.total = sourceByteLength;

  // It is important that the nextYieldAccumulation is not greater than source length, so that
  // it can be used as boundary checking to reduce checking times and increase performance.
  let nextYieldAccumulation = Math.min(sourceByteLength, progressToAdvance.accumulation + suspendByteCount);

  // 1. Skip specified lines.
  {
    let skippedLineCount = 0;

    while (progressToAdvance.accumulation < sourceByteLength) {
      if (skippedLineCount >= skipLineCount)
        break;                 // Already skip enough lines.

      // (This inner loop combines both source and yield boundary checking. Reducing checking to increase performance.)
      while (progressToAdvance.accumulation < nextYieldAccumulation) {
        if (skippedLineCount >= skipLineCount)
          break;                 // Already skip enough lines.

        let rawByte = sourceBytes[ progressToAdvance.accumulation++ ];

        if (13 == rawByte) {     // "\r" (carriage return; CR)
          ++skippedLineCount;    // One line is skipped.

          // If a LF follows a CR, it is considered as CRLF sequence and viewed as the same one line.
          //
          // Note: It may exceed the nextYieldAccumulation boundary. But should not exceed sourceByteLength.
          if (   (progressToAdvance.accumulation < sourceByteLength)
              && (10 == sourceBytes[ progressToAdvance.accumulation ])
             ) {
            ++progressToAdvance.accumulation; // Skip it.
          }

        } else {
          if (10 == rawByte)    // "\n" (new line; LF)
            ++skippedLineCount; // One line is skipped.
        }
      }

      // Every suspendByteCount, release CPU time (and report progress).
      if (progressToAdvance.accumulation >= nextYieldAccumulation) {
        nextYieldAccumulation = Math.min(sourceByteLength, progressToAdvance.accumulation + suspendByteCount);
        yield progressToYield;
      }
    }
  }

  // 2. Decode.

  // Decoding base64 will result in a shorter data (about 75% (= 3 / 4) in size).
  let possibleBase64ByteLength = (sourceByteLength - progressToAdvance.accumulation);  // Forget the skipped lines.
  let targetByteLength = Math.ceil(possibleBase64ByteLength * 0.75);
  let targetArrayBuffer = new ArrayBuffer( targetByteLength );
  let targetBytes = new Uint8Array( targetArrayBuffer );

  let resultByteCount = 0;  // Accumulate the real result byte count.

  {
    while (progressToAdvance.accumulation < sourceByteLength) {

      nextYieldLoop:

      // (This inner loop combines both source and yield boundary checking. Reducing checking to increase performance.)
      while (progressToAdvance.accumulation < nextYieldAccumulation) {

        // Extract 4 source bytes. (A decode unit consists of 4 base64 encoded source bytes.)
        //
        // Although it is verbose to loop unrolling manually, it is far more faster
        // to use 4 local variables than use a 4-element normal array. (Note: the
        // 4-element normal array is far more faster than a Uint8Array() again).

        let encoded_0;
        do {
          // Note: It may exceed the nextYieldAccumulation boundary. But should not exceed sourceByteLength.
          if (progressToAdvance.accumulation >= sourceByteLength)
            break nextYieldLoop; // Decoding is done. (Ignore last non-4-bytes.)

          encoded_0 = table_base64_Uint8_to_index[ sourceBytes[ progressToAdvance.accumulation++ ] ];
        } while (255 === encoded_0);

        let encoded_1;
        do {
          // Note: It may exceed the nextYieldAccumulation boundary. But should not exceed sourceByteLength.
          if (progressToAdvance.accumulation >= sourceByteLength)
            break nextYieldLoop; // Decoding is done. (Ignore last non-4-bytes.)

          encoded_1 = table_base64_Uint8_to_index[ sourceBytes[ progressToAdvance.accumulation++ ] ];
        } while (255 === encoded_1);

        let encoded_2;
        do {
          // Note: It may exceed the nextYieldAccumulation boundary. But should not exceed sourceByteLength.
          if (progressToAdvance.accumulation >= sourceByteLength)
            break nextYieldLoop; // Decoding is done. (Ignore last non-4-bytes.)

          encoded_2 = table_base64_Uint8_to_index[ sourceBytes[ progressToAdvance.accumulation++ ] ];
        } while (255 === encoded_2);

        let encoded_3;
        do {
          // Note: It may exceed the nextYieldAccumulation boundary. But should not exceed sourceByteLength.
          if (progressToAdvance.accumulation >= sourceByteLength)
            break nextYieldLoop; // Decoding is done. (Ignore last non-4-bytes.)

          encoded_3 = table_base64_Uint8_to_index[ sourceBytes[ progressToAdvance.accumulation++ ] ];
        } while (255 === encoded_3);

        targetBytes[resultByteCount++] =  (encoded_0       << 2) | (encoded_1 >> 4);
        targetBytes[resultByteCount++] = ((encoded_1 & 15) << 4) | (encoded_2 >> 2);
        targetBytes[resultByteCount++] = ((encoded_2 &  3) << 6) | (encoded_3 & 63);
      }

      // Every suspendByteCount, release CPU time (and report progress).
      if (progressToAdvance.accumulation >= nextYieldAccumulation) {
        nextYieldAccumulation = Math.min(sourceByteLength, progressToAdvance.accumulation + suspendByteCount);
        yield progressToYield;
      }

    }
  }

  // 3. Result.

  // The resultBytes is a sub-range of target buffer.
  //
  // Because the source may have some non-base64 codes which will be ignored,
  // the result data may be less than target length.
  let resultBytes = new Uint8Array( targetArrayBuffer, 0, resultByteCount );

  yield progressToYield; // Report the progress has been done (100%).

  return resultBytes;
}
<commit_msg>Improve comments.<commit_after>export { decoder };

const base64String = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

// Mapping table for base64 (as Uint8) to index.
let table_base64_Uint8_to_index = new Array(256); // Faster than using Uint8Array().
{
  // For all non-base64 codes, using value greater than 63 (i.e. impossible base64) for identifying them.
  for (let i = 0; i < table_base64_Uint8_to_index.length; ++i)
    table_base64_Uint8_to_index[ i ] = 255;

  // For all legal base64 codes, using value between [0, 63].
  {
    for (let i = 0; i < base64String.length; ++i) {
      let codePoint = base64String.codePointAt(i);
      table_base64_Uint8_to_index[ codePoint ] = i;
    }

    // Support decoding URL-safe base64 strings, as Node.js does.
    // See: https://en.wikipedia.org/wiki/Base64#URL_applications
    table_base64_Uint8_to_index['-'.charCodeAt(0)] = 62;
    table_base64_Uint8_to_index['_'.charCodeAt(0)] = 63;
  }
}

/**
 *
 * @param {ArrayBuffer} sourceBase64ArrayBuffer
 *   The input base64 data as ArrayBuffer. If the last bytes not enough 4 bytes, they will be discarded (will
 * not be decoded). If an input byte is not a legal base64 code (i.e. not A..Z, a..z, 0..9, +, /, -, _), the
 * byte will be skipped (as if it does not exist). So the input bytes can be separated by new line.
 *
 * @param {Uint32} skipLineCount
 *   Skip how many lines in the source before decoding.
 *
 * @param {ValueMax.Percentage.Aggregate} progressToYield
 *   Return this when every time yield. Usually, this is the container of the progressToAdvance.
 *
 * @param {ValueMax.Percentage.Concrete}  progressToAdvance
 *   Increase this when every time advanced.
 *
 * @param {Uint32} suspendByteCount
 *   Everytime so many bytes decoded, yield for releasing CPU time (and reporting progress).
 *   Default is 1024 bytes.
 *
 * @yield {ValueMax.Percentage.Aggregate or Uint8Array}
 *   Yield ( value = progressYield ) when ( done = false ).
 *   Yield ( value = decoded data as Uint8Array ) when ( done = true ).
 */
function* decoder(
  sourceBase64ArrayBuffer, skipLineCount, progressToYield, progressToAdvance, suspendByteCount) {

  // 0. Initialize.

  // If undefined or null or negative or zero or less than 1, set to default.
  // Note: Bitwising OR with zero is for converting to integer (if it is undefined or null).
  if ((suspendByteCount | 0) <= 0)
    suspendByteCount = 1024;

  let sourceByteLength = sourceBase64ArrayBuffer.byteLength;
  let sourceBytes = new Uint8Array( sourceBase64ArrayBuffer );

  // Initialize progress.
  progressToAdvance.accumulation = 0;
  progressToAdvance.total = sourceByteLength;

  // It is important that the nextYieldAccumulation is not greater than source length, so that
  // it can be used as boundary checking to reduce checking times and increase performance.
  let nextYieldAccumulation = Math.min(sourceByteLength, progressToAdvance.accumulation + suspendByteCount);

  // 1. Skip specified lines.
  {
    let skippedLineCount = 0;

    while (progressToAdvance.accumulation < sourceByteLength) {
      if (skippedLineCount >= skipLineCount)
        break;                 // Already skip enough lines.

      // (This inner loop combines both source and yield boundary checking. Reducing checking to increase performance.)
      while (progressToAdvance.accumulation < nextYieldAccumulation) {
        if (skippedLineCount >= skipLineCount)
          break;                 // Already skip enough lines.

        let rawByte = sourceBytes[ progressToAdvance.accumulation++ ];

        if (13 == rawByte) {     // "\r" (carriage return; CR)
          ++skippedLineCount;    // One line is skipped.

          // If a LF follows a CR, it is considered as CRLF sequence and viewed as the same one line.
          //
          // Note: It may exceed the nextYieldAccumulation boundary. But should not exceed sourceByteLength.
          if (   (progressToAdvance.accumulation < sourceByteLength)
              && (10 == sourceBytes[ progressToAdvance.accumulation ])
             ) {
            ++progressToAdvance.accumulation; // Skip it.
          }

        } else {
          if (10 == rawByte)    // "\n" (new line; LF)
            ++skippedLineCount; // One line is skipped.
        }
      }

      // Every suspendByteCount, release CPU time (and report progress).
      if (progressToAdvance.accumulation >= nextYieldAccumulation) {
        nextYieldAccumulation = Math.min(sourceByteLength, progressToAdvance.accumulation + suspendByteCount);
        yield progressToYield;
      }
    }
  }

  // 2. Decode.

  // Decoding base64 will result in a shorter data (about 75% (= 3 / 4) in size).
  let possibleBase64ByteLength = (sourceByteLength - progressToAdvance.accumulation);  // Forget the skipped lines.
  let targetByteLength = Math.ceil(possibleBase64ByteLength * 0.75);
  let targetArrayBuffer = new ArrayBuffer( targetByteLength );
  let targetBytes = new Uint8Array( targetArrayBuffer );

  let resultByteCount = 0;  // Accumulate the real result byte count.

  {
    while (progressToAdvance.accumulation < sourceByteLength) {

      nextYieldLoop:

      // (This inner loop combines both source and yield boundary checking. Reducing checking to increase performance.)
      while (progressToAdvance.accumulation < nextYieldAccumulation) {

        // Extract 4 source bytes. (A decode unit consists of 4 base64 encoded source bytes.)
        //
        // Although it is verbose to loop unrolling manually, it is far more faster
        // to use 4 local variables than use a 4-element normal array. (Note: the
        // 4-element normal array is far more faster than a Uint8Array() again).

        let encoded_0;
        do {
          // Note: It may exceed the nextYieldAccumulation boundary. But should not exceed sourceByteLength.
          if (progressToAdvance.accumulation >= sourceByteLength)
            break nextYieldLoop; // Decoding is done. (Ignore last non-4-bytes.)

          encoded_0 = table_base64_Uint8_to_index[ sourceBytes[ progressToAdvance.accumulation++ ] ];
        } while (255 === encoded_0);

        let encoded_1;
        do {
          // Note: It may exceed the nextYieldAccumulation boundary. But should not exceed sourceByteLength.
          if (progressToAdvance.accumulation >= sourceByteLength)
            break nextYieldLoop; // Decoding is done. (Ignore last non-4-bytes.)

          encoded_1 = table_base64_Uint8_to_index[ sourceBytes[ progressToAdvance.accumulation++ ] ];
        } while (255 === encoded_1);

        let encoded_2;
        do {
          // Note: It may exceed the nextYieldAccumulation boundary. But should not exceed sourceByteLength.
          if (progressToAdvance.accumulation >= sourceByteLength)
            break nextYieldLoop; // Decoding is done. (Ignore last non-4-bytes.)

          encoded_2 = table_base64_Uint8_to_index[ sourceBytes[ progressToAdvance.accumulation++ ] ];
        } while (255 === encoded_2);

        let encoded_3;
        do {
          // Note: It may exceed the nextYieldAccumulation boundary. But should not exceed sourceByteLength.
          if (progressToAdvance.accumulation >= sourceByteLength)
            break nextYieldLoop; // Decoding is done. (Ignore last non-4-bytes.)

          encoded_3 = table_base64_Uint8_to_index[ sourceBytes[ progressToAdvance.accumulation++ ] ];
        } while (255 === encoded_3);

        targetBytes[resultByteCount++] =  (encoded_0       << 2) | (encoded_1 >> 4);
        targetBytes[resultByteCount++] = ((encoded_1 & 15) << 4) | (encoded_2 >> 2);
        targetBytes[resultByteCount++] = ((encoded_2 &  3) << 6) | (encoded_3 & 63);
      }

      // Every suspendByteCount, release CPU time (and report progress).
      if (progressToAdvance.accumulation >= nextYieldAccumulation) {
        nextYieldAccumulation = Math.min(sourceByteLength, progressToAdvance.accumulation + suspendByteCount);
        yield progressToYield;
      }

    }
  }

  // 3. Result.

  // The resultBytes is a sub-range of target buffer.
  //
  // Because the source may have some non-base64 codes which will be ignored,
  // the result data may be less than target length.
  let resultBytes = new Uint8Array( targetArrayBuffer, 0, resultByteCount );

  yield progressToYield; // Report the progress has been done (100%).

  return resultBytes;
}
<|endoftext|>


<commit_before>'use strict';

/**
 * @ngdoc function
 * @name diddleplanApp.controller:CalendarCtrl
 * @description
 * # CalendarCtrl
 * Controller of the diddleplanApp
 */
angular.module('diddleplanApp')
  .controller('CalendarCtrl', function ($scope, tasks, TaskData, ExerciseService) {

    $scope.convertRecurrences = function(task) {
      if (task.TaskRecurrences === undefined) {
        return;
      }

      for (var i = 0; i < task.TaskRecurrences.length; i++) {
        var r = task.TaskRecurrences[i];
        var r_task = {
          name: task.name,
          taskType: task.taskType,
          date: new Date(r.date).getTime(),
          time: r.time,
          moveable: false,
          autoMoveable: false,
          isRecurring: true,
          isRecurrence: true,
          TaskTaskID: task.taskID,
          rID: r.recurrenceID
        };

        $scope.tasks.push(r_task);
      }
    };

    $scope.removeRecurrences = function(task) {
      for (var i = 0; i < $scope.tasks.length; ++i) {
        if (!$scope.tasks[i].isRecurrence) {
          continue;
        }

        if ($scope.tasks[i].TaskTaskID === task.taskID) {
          $scope.tasks.splice(i, 1);
        }
      }
    };

    $scope.removeSingleRecurrence = function(recurrence) {
      for (var i = 0; i < $scope.tasks.length; ++i) {
        if (!$scope.tasks[i].isRecurrence) {
          continue;
        }

        if ($scope.tasks[i].rID === recurrence.rID) {
          $scope.tasks.splice(i, 1);
        }
      }
    };

    var roundDateToDay = function(date) {
      date.setHours(0);
      date.setMinutes(0);
      date.setSeconds(0);
      date.setMilliseconds(0);
      return date;
    };

    $scope.tasks = tasks.data;
    for (var i = 0; i < $scope.tasks.length; i++) {
      $scope.convertRecurrences($scope.tasks[i]);
    }
    $scope.types = ['todo', 'exercise', 'meal', 'shopping'];

    $scope.removeTask = function(task) {
      for (var i = 0; i < $scope.tasks.length; ++i) {
        if ($scope.tasks[i].name === task.name && $scope.tasks[i].date === task.date) {
          $scope.tasks.splice(i, 1);
          break;
        }
      }
    };

    // create some dates
    $scope.calendar = [];
    $scope.today = roundDateToDay(new Date).getTime();
    var oneDay = 86400000;
    for (var j = -6; j < 12; ++j) {
      $scope.calendar.push($scope.today + oneDay * j);
    }
    var now = new Date();
    $scope.lastYear = new Date(now.getFullYear() - 1, 0, 1).getTime();
    $scope.nextYear = new Date(now.getFullYear() + 1, 0, 1).getTime();

    $scope.loadPastDays = function() {
      var last = $scope.calendar[0];
      for (var i = -1; i >= -9; --i) {
        $scope.calendar.unshift(last + (oneDay * i));
      }
    };

    $scope.loadFutureDays = function() {
      var last = $scope.calendar[$scope.calendar.length - 1];
      for (var i = 1; i <= 9; ++i) {
        $scope.calendar.push(last + (oneDay * i));
      }
    };

    $scope.addTask = function(type, date) {
      var d = roundDateToDay(new Date(task.date));

      // TODO: Need to pass in or work these out
      var newTaskData = {
        "name": "",
        "taskType": type,
        "date": d.getTime(),
        "moveable": true,
        "autoMoveable": null,
        "UserId": 1
      };

      TaskData.addTask(newTaskData)
        .success(function(response) {
          $scope.tasks.push(response);
        })
        .error(function(response) {
          console.log(response);
        });
    };

    $scope.updateTask = function(task) {
      var timeRe = /^\s*([01]\d|2[0-3]):([0-5]\d)\s*$/;

      if (task.time !== null && task.time !== "") {
        if (!task.time.match(timeRe)) {
          task.validTime = false;
          return;
        }
      } else if (task.time === "") {
        task.time = null;
      }

      task.validTime = true;

      TaskData.updateTask(task).error(function(response) {
        console.log(response);
      });
    };

    $scope.deleteTask = function(task) {
      if (task.isRecurrence) {
        TaskData.deleteRecurrence(task).success(function() {
          $scope.removeSingleRecurrence(task);
        }).error(function(response) {
          console.log(response);
        });
      } else {
        TaskData.deleteTask(task).success(function() {
          $scope.removeTask(task);
        }).error(function(response) {
          console.log(response);
        });
      }
    };

    var shoppingItem = {
      name: 'Shopping',
      type: 'shopping'
    };

    $scope.calculateShoppingTrip = function() {
      var remainingMeals = $scope.meals.length;
      for (var i = 0; i < remainingMeals; ++i) {
        var items = $scope.calendar[i].items;

        for (var j = 0; j < items; ++j) {
          if (items[j].taskType === 'meal') {
            remainingMeals++;
          }
        }
      }

      $scope.calendar[remainingMeals].items.push(shoppingItem);
    };

    $scope.getNewExercises = function() {
      ExerciseService.getExercises().success(function() {
        TaskData.getTasks().success(function(tasks) {
          $scope.tasks = tasks;
          for (var i = 0; i < $scope.tasks.length; i++) {
            $scope.convertRecurrences($scope.tasks[i]);
          }
        });
      });
    };
    $scope.getNewExercises();

    $scope.addRecurrence = function(task) {
      TaskData.recurTask(task).success(function(response) {
        task.TaskRecurrences = response.task.TaskRecurrences;
        task.isRecurring = true;
        $scope.convertRecurrences(task);
      });
    };

    $scope.removeRecurrence = function(task) {
      TaskData.unrecurTask(task).success(function() {
        task.TaskRecurrences = [];
        task.isRecurring = false;
        task.recRange = undefined;
        task.recPeriod = undefined;
        $scope.removeRecurrences(task);
      });
    };

    $scope.updateRecurrence = function(task) {
      var tempRange = task.recRange;
      var tempPeriod = task.recPeriod;

      $scope.removeRecurrence(task);

      task.recRange = tempRange;
      task.recPeriod = tempPeriod;
      $scope.addRecurrence(task);
    };

    $scope.isTodo = function(value) {
      return (value.taskType === 'todo') && !value.date;
    };

    $scope.isExercise = function(value) {
      return (value.taskType === 'exercise') && !value.date;
    };

    $scope.isMeal = function(value) {
      return (value.taskType === 'meal') && !value.date;
    };

    $scope.isShopping = function(value) {
      return (value.taskType === 'shopping') && !value.date;
    };

    $scope.hasDate = function(value, date) {
      return value && value.date === date;
    };

    $scope.detailView = {};
    $scope.tasksForDate = {};
    $scope.showDetailView = function(task) {
      var d = roundDateToDay(new Date(task.date));

      $scope.detailView[d.getTime()] = 1;
      $scope.tasksForDate[d.getTime()] = task;
    };

    $scope.hideDetailView = function(day) {
      var d = roundDateToDay(new Date(day));

      $scope.detailView[d.getTime()] = undefined;
      $scope.tasksForDate[d.getTime()] = undefined;
    };

  });
<commit_msg>Fix a couple of silly bugs<commit_after>'use strict';

/**
 * @ngdoc function
 * @name diddleplanApp.controller:CalendarCtrl
 * @description
 * # CalendarCtrl
 * Controller of the diddleplanApp
 */
angular.module('diddleplanApp')
  .controller('CalendarCtrl', function ($scope, tasks, TaskData, ExerciseService) {

    $scope.convertRecurrences = function(task) {
      if (task.TaskRecurrences === undefined) {
        return;
      }

      for (var i = 0; i < task.TaskRecurrences.length; i++) {
        var r = task.TaskRecurrences[i];
        var r_task = {
          name: task.name,
          taskType: task.taskType,
          date: new Date(r.date).getTime(),
          time: r.time,
          moveable: false,
          autoMoveable: false,
          isRecurring: true,
          isRecurrence: true,
          TaskTaskID: task.taskID,
          rID: r.recurrenceID
        };

        $scope.tasks.push(r_task);
      }
    };

    $scope.removeRecurrences = function(task) {
      for (var i = 0; i < $scope.tasks.length; ++i) {
        if (!$scope.tasks[i].isRecurrence) {
          continue;
        }

        if ($scope.tasks[i].TaskTaskID === task.taskID) {
          $scope.tasks.splice(i, 1);
        }
      }
    };

    $scope.removeSingleRecurrence = function(recurrence) {
      for (var i = 0; i < $scope.tasks.length; ++i) {
        if (!$scope.tasks[i].isRecurrence) {
          continue;
        }

        if ($scope.tasks[i].rID === recurrence.rID) {
          $scope.tasks.splice(i, 1);
        }
      }
    };

    var roundDateToDay = function(date) {
      date.setHours(0);
      date.setMinutes(0);
      date.setSeconds(0);
      date.setMilliseconds(0);
      return date;
    };

    $scope.tasks = tasks.data;
    for (var i = 0; i < $scope.tasks.length; i++) {
      $scope.convertRecurrences($scope.tasks[i]);
    }
    $scope.types = ['todo', 'exercise', 'meal', 'shopping'];

    $scope.removeTask = function(task) {
      for (var i = 0; i < $scope.tasks.length; ++i) {
        if ($scope.tasks[i].name === task.name && $scope.tasks[i].date === task.date) {
          $scope.tasks.splice(i, 1);
          break;
        }
      }
    };

    // create some dates
    $scope.calendar = [];
    $scope.today = roundDateToDay(new Date()).getTime();
    var oneDay = 86400000;
    for (var j = -6; j < 12; ++j) {
      $scope.calendar.push($scope.today + oneDay * j);
    }
    var now = new Date();
    $scope.lastYear = new Date(now.getFullYear() - 1, 0, 1).getTime();
    $scope.nextYear = new Date(now.getFullYear() + 1, 0, 1).getTime();

    $scope.loadPastDays = function() {
      var last = $scope.calendar[0];
      for (var i = -1; i >= -9; --i) {
        $scope.calendar.unshift(last + (oneDay * i));
      }
    };

    $scope.loadFutureDays = function() {
      var last = $scope.calendar[$scope.calendar.length - 1];
      for (var i = 1; i <= 9; ++i) {
        $scope.calendar.push(last + (oneDay * i));
      }
    };

    $scope.addTask = function(type, date) {
      var d = roundDateToDay(new Date(date));

      // TODO: Need to pass in or work these out
      var newTaskData = {
        "name": "",
        "taskType": type,
        "date": d.getTime(),
        "moveable": true,
        "autoMoveable": null,
        "UserId": 1
      };

      TaskData.addTask(newTaskData)
        .success(function(response) {
          $scope.tasks.push(response);
        })
        .error(function(response) {
          console.log(response);
        });
    };

    $scope.updateTask = function(task) {
      var timeRe = /^\s*([01]\d|2[0-3]):([0-5]\d)\s*$/;

      if (task.time !== null && task.time !== "") {
        if (!task.time.match(timeRe)) {
          task.validTime = false;
          return;
        }
      } else if (task.time === "") {
        task.time = null;
      }

      task.validTime = true;

      TaskData.updateTask(task).error(function(response) {
        console.log(response);
      });
    };

    $scope.deleteTask = function(task) {
      if (task.isRecurrence) {
        TaskData.deleteRecurrence(task).success(function() {
          $scope.removeSingleRecurrence(task);
        }).error(function(response) {
          console.log(response);
        });
      } else {
        TaskData.deleteTask(task).success(function() {
          $scope.removeTask(task);
        }).error(function(response) {
          console.log(response);
        });
      }
    };

    var shoppingItem = {
      name: 'Shopping',
      type: 'shopping'
    };

    $scope.calculateShoppingTrip = function() {
      var remainingMeals = $scope.meals.length;
      for (var i = 0; i < remainingMeals; ++i) {
        var items = $scope.calendar[i].items;

        for (var j = 0; j < items; ++j) {
          if (items[j].taskType === 'meal') {
            remainingMeals++;
          }
        }
      }

      $scope.calendar[remainingMeals].items.push(shoppingItem);
    };

    $scope.getNewExercises = function() {
      ExerciseService.getExercises().success(function() {
        TaskData.getTasks().success(function(tasks) {
          $scope.tasks = tasks;
          for (var i = 0; i < $scope.tasks.length; i++) {
            $scope.convertRecurrences($scope.tasks[i]);
          }
        });
      });
    };
    $scope.getNewExercises();

    $scope.addRecurrence = function(task) {
      TaskData.recurTask(task).success(function(response) {
        task.TaskRecurrences = response.task.TaskRecurrences;
        task.isRecurring = true;
        $scope.convertRecurrences(task);
      });
    };

    $scope.removeRecurrence = function(task) {
      TaskData.unrecurTask(task).success(function() {
        task.TaskRecurrences = [];
        task.isRecurring = false;
        task.recRange = undefined;
        task.recPeriod = undefined;
        $scope.removeRecurrences(task);
      });
    };

    $scope.updateRecurrence = function(task) {
      var tempRange = task.recRange;
      var tempPeriod = task.recPeriod;

      $scope.removeRecurrence(task);

      task.recRange = tempRange;
      task.recPeriod = tempPeriod;
      $scope.addRecurrence(task);
    };

    $scope.isTodo = function(value) {
      return (value.taskType === 'todo') && !value.date;
    };

    $scope.isExercise = function(value) {
      return (value.taskType === 'exercise') && !value.date;
    };

    $scope.isMeal = function(value) {
      return (value.taskType === 'meal') && !value.date;
    };

    $scope.isShopping = function(value) {
      return (value.taskType === 'shopping') && !value.date;
    };

    $scope.hasDate = function(value, date) {
      return value && value.date === date;
    };

    $scope.detailView = {};
    $scope.tasksForDate = {};
    $scope.showDetailView = function(task) {
      var d = roundDateToDay(new Date(task.date));

      $scope.detailView[d.getTime()] = 1;
      $scope.tasksForDate[d.getTime()] = task;
    };

    $scope.hideDetailView = function(day) {
      var d = roundDateToDay(new Date(day));

      $scope.detailView[d.getTime()] = undefined;
      $scope.tasksForDate[d.getTime()] = undefined;
    };

  });
<|endoftext|>


<commit_before>export { decoder };

const base64String = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

// Mapping table for base64 (as Uint8) to index.
let table_base64_Uint8_to_index = new Array(256); // Faster than using Uint8Array().
{
  // For all non-base64 codes, using value greater than 63 (i.e. impossible base64) for identifying them.
  for (let i = 0; i < table_base64_Uint8_to_index.length; ++i)
    table_base64_Uint8_to_index[ i ] = 255;

  // For all legal base64 codes, using value between [0, 63].
  {
    for (let i = 0; i < base64String.length; ++i) {
      let codePoint = base64String.codePointAt(i);
      table_base64_Uint8_to_index[ codePoint ] = i;
    }

    // Support decoding URL-safe base64 strings, as Node.js does.
    // See: https://en.wikipedia.org/wiki/Base64#URL_applications
    table_base64_Uint8_to_index['-'.charCodeAt(0)] = 62;
    table_base64_Uint8_to_index['_'.charCodeAt(0)] = 63;
  }
}

/**
 *
 * @param {ArrayBuffer} sourceBase64ArrayBuffer
 *   The input base64 data as ArrayBuffer. If the last bytes not enough 4 bytes, they will be discarded (will
 * not be decoded). If an input byte is not a legal base64 code (i.e. not A..Z, a..z, 0..0, +, /), the byte
 * will be skipped (as if it does not exist). So the input bytes can be separated by new line.
 *
 * @param {Uint32} skipLineCount
 *   Skip how many lines in the source before decoding.
 *
 * @param {ValueMax.Percentage.Aggregate} progressToYield
 *   Return this when every time yield.
 *
 * @param {ValueMax.Percentage.Concrete}  progressToAdvance
 *   Increase this when every time advanced.
 *
 * @param {Uint32} suspendByteCount
 *   Everytime so many bytes decoded, yield for releasing CPU time (and reporting progress).
 *   Default is 1024 bytes.
 *
 * @yield {ValueMax.Percentage.Aggregate or Uint8Array}
 *   Yield ( value = progressYield ) when ( done = false ).
 *   Yield ( value = decoded data as Uint8Array ) when ( done = true ).
 */
function* decoder(
  sourceBase64ArrayBuffer, skipLineCount, progressToYield, progressToAdvance, suspendByteCount) {

  // 0. Initialize.

  let sourceByteLength = sourceBase64ArrayBuffer.byteLength;
  let sourceBytes = new Uint8Array( sourceBase64ArrayBuffer );

//!!! (2019/06/14 Remarked)
// //  suspendByteCount = Math.max(suspendByteCount | 0, Math.min(1024, sourceByteLength));
//
// //  suspendByteCount |= 0;  // Bitwising OR with zero is for converting to integer (if it is undefined or null).
//
//   // Ensure between [0, min(1024, sourceByteLength)].
//   //
//   // It is important that the suspendByteCount is not greater than source length. So the
//   // nextYieldAccumulation can be used for boundary checking.
// //!!!
// //  suspendByteCount = Math.max(0, Math.min(1024, sourceByteLength));
//   suspendByteCount = Math.max(0, Math.max(suspendByteCount | 0, Math.min(1024, sourceByteLength)));

  // Ensure between [0, min(1024, sourceByteLength)].
  //
  // It is important that the suspendByteCount is not greater than source length. So the
  // nextYieldAccumulation can be used as boundary checking to reduce checking times and increase performance.
  {
    // If undefined or null or negative or zero or less than 1, set to default.
    if ((suspendByteCount | 0) <= 0)
      suspendByteCount = 1024;
    if (suspendByteCount > sourceByteLength)
      suspendByteCount = sourceByteLength;
  }

  // Initialize progress.
  progressToAdvance.accumulation = 0;
  progressToAdvance.total = sourceByteLength;

//!!!
//  let yieldCount = 0;
  let nextYieldAccumulation = suspendByteCount;

  // 1. Skip specified lines.
  {
    let skippedLineCount = 0;

    while (progressToAdvance.accumulation < sourceByteLength) {
      if (skippedLineCount >= skipLineCount)
        break;                 // Already skip enough lines.

//!!!
      // (This inner loop combines both source and yield boundary checking. Reducing checking to increase performance.)
      while (progressToAdvance.accumulation < nextYieldAccumulation) {
        if (skippedLineCount >= skipLineCount)
          break;                 // Already skip enough lines.

        let rawByte = sourceBytes[ progressToAdvance.accumulation++ ];

        if (13 == rawByte) {     // "\r" (carriage return; CR)
          ++skippedLineCount;    // One line is skipped.

          // If a LF follows a CR, it is considered as CRLF sequence and viewed as the same one line.
          //
          // Note: It may exceed the nextYieldAccumulation boundary. But should not exceed sourceByteLength.
          if (   (progressToAdvance.accumulation < sourceByteLength)
              && (10 == sourceBytes[ progressToAdvance.accumulation ])
             ) {
            ++progressToAdvance.accumulation; // Skip it.
          }

        } else {
          if (10 == rawByte)    // "\n" (new line; LF)
            ++skippedLineCount; // One line is skipped.
        }
      }
// !!!
//       // Every suspendByteCount, release CPU time (and report progress).
//       if (progressToAdvance.accumulation >= nextYieldAccumulation) {
//         nextYieldAccumulation = progressToAdvance.accumulation + suspendByteCount;
//         yield progressToYield;
//       }

      // Every suspendByteCount, release CPU time (and report progress).
      if (progressToAdvance.accumulation >= nextYieldAccumulation) {
        nextYieldAccumulation = progressToAdvance.accumulation + suspendByteCount;

        if (nextYieldAccumulation > sourceByteLength)
          nextYieldAccumulation = sourceByteLength;

//!!!
//        ++yieldCount;
        yield progressToYield;
      }
    }
  }

  // 2. Decode.

  // Decoding base64 will result in a shorter data (about 75% (= 3 / 4) in size).
  let possibleBase64ByteLength = (sourceByteLength - progressToAdvance.accumulation);  // Forget the skipped lines.
  let targetByteLength = Math.ceil(possibleBase64ByteLength * 0.75);
  let targetArrayBuffer = new ArrayBuffer( targetByteLength );
  let targetBytes = new Uint8Array( targetArrayBuffer );

  let resultByteCount = 0;  // Accumulate the real result byte count.

  {
    const BYTES_PER_DECODE_UNIT = 4; // A decode unit consists of 4 base64 encoded source bytes.
    let encodedBytes = new Array( BYTES_PER_DECODE_UNIT ); // Faster than using Uint8Array().

    while (progressToAdvance.accumulation < sourceByteLength) {

//!!!
      // (This inner loop combines both source and yield boundary checking. Reducing checking to increase performance.)
      while (progressToAdvance.accumulation < nextYieldAccumulation) {

        // Extract 4 source bytes.
        let j = 0;
        while (j < BYTES_PER_DECODE_UNIT) {

          // Note: It may exceed the nextYieldAccumulation boundary. But should not exceed sourceByteLength.
          if (progressToAdvance.accumulation >= sourceByteLength)
            break; // Decoding is done. (Ignore last non-4-bytes.)

          encodedBytes[ j ] = table_base64_Uint8_to_index[ sourceBytes[ progressToAdvance.accumulation++ ] ];

          if (255 === encodedBytes[ j ])
            continue; // Skip any non-base64 bytes.

          ++j;
        }

        if (j != BYTES_PER_DECODE_UNIT)
          break; // Decoding is done. (Ignore last non-4-bytes.)

        targetBytes[resultByteCount++] =  (encodedBytes[ 0 ]       << 2) | (encodedBytes[ 1 ] >> 4);
        targetBytes[resultByteCount++] = ((encodedBytes[ 1 ] & 15) << 4) | (encodedBytes[ 2 ] >> 2);
        targetBytes[resultByteCount++] = ((encodedBytes[ 2 ] &  3) << 6) | (encodedBytes[ 3 ] & 63);
      }

//!!!
//       // Every suspendByteCount, release CPU time (and report progress).
//       if (progressToAdvance.accumulation >= nextYieldAccumulation) {
//         nextYieldAccumulation = progressToAdvance.accumulation + suspendByteCount;
//         yield progressToYield;
//       }

      // Every suspendByteCount, release CPU time (and report progress).
      if (progressToAdvance.accumulation >= nextYieldAccumulation) {
        nextYieldAccumulation = progressToAdvance.accumulation + suspendByteCount;

        if (nextYieldAccumulation > sourceByteLength)
          nextYieldAccumulation = sourceByteLength;
//!!!
//        ++yieldCount;
        yield progressToYield;
      }

    }
  }

  // 3. Result.

  // The resultBytes is a sub-range of target buffer.
  //
  // Because the source may have some non-base64 codes which will be ignored,
  // the result data may be less than target length.
  let resultBytes = new Uint8Array( targetArrayBuffer, 0, resultByteCount );

//!!! (2019/06/14) Remarked. not feasible.
//   if (   // Never yield (i.e. never report progress), report at least once for done.
//          (nextYieldAccumulation == suspendByteCount)
//
//          // Or, some advance has been made after last progress report, report for done.
//       || ((nextYieldAccumulation - progressToAdvance.accumulation) < suspendByteCount)
//      )
//     yield progressToYield; // Report the progress has been done (100%).
//   else
//     ; // The last progress report is (just luckily) 100%. No need to report the progress again.

//!!! (2019/06/14) Remarked. not workable.
//   if (   // Never yield (i.e. never report progress), report at least once (for done).
//          (0 == yieldCount)
// !!! wrong
//          // Or, some advance has been made after last progress report, report for done.
//       || ((nextYieldAccumulation - progressToAdvance.accumulation) < suspendByteCount)
//      )
//     yield progressToYield; // Report the progress has been done (100%).
//   else
//     ; // The last progress report is (just luckily) 100%. No need to report the progress again.

  yield progressToYield; // Report the progress has been done (100%).

  return resultBytes;
}
<commit_msg>Update Base64ArrayBufferToUint8Array.js<commit_after>export { decoder };

const base64String = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

// Mapping table for base64 (as Uint8) to index.
let table_base64_Uint8_to_index = new Array(256); // Faster than using Uint8Array().
{
  // For all non-base64 codes, using value greater than 63 (i.e. impossible base64) for identifying them.
  for (let i = 0; i < table_base64_Uint8_to_index.length; ++i)
    table_base64_Uint8_to_index[ i ] = 255;

  // For all legal base64 codes, using value between [0, 63].
  {
    for (let i = 0; i < base64String.length; ++i) {
      let codePoint = base64String.codePointAt(i);
      table_base64_Uint8_to_index[ codePoint ] = i;
    }

    // Support decoding URL-safe base64 strings, as Node.js does.
    // See: https://en.wikipedia.org/wiki/Base64#URL_applications
    table_base64_Uint8_to_index['-'.charCodeAt(0)] = 62;
    table_base64_Uint8_to_index['_'.charCodeAt(0)] = 63;
  }
}

/**
 *
 * @param {ArrayBuffer} sourceBase64ArrayBuffer
 *   The input base64 data as ArrayBuffer. If the last bytes not enough 4 bytes, they will be discarded (will
 * not be decoded). If an input byte is not a legal base64 code (i.e. not A..Z, a..z, 0..0, +, /), the byte
 * will be skipped (as if it does not exist). So the input bytes can be separated by new line.
 *
 * @param {Uint32} skipLineCount
 *   Skip how many lines in the source before decoding.
 *
 * @param {ValueMax.Percentage.Aggregate} progressToYield
 *   Return this when every time yield.
 *
 * @param {ValueMax.Percentage.Concrete}  progressToAdvance
 *   Increase this when every time advanced.
 *
 * @param {Uint32} suspendByteCount
 *   Everytime so many bytes decoded, yield for releasing CPU time (and reporting progress).
 *   Default is 1024 bytes.
 *
 * @yield {ValueMax.Percentage.Aggregate or Uint8Array}
 *   Yield ( value = progressYield ) when ( done = false ).
 *   Yield ( value = decoded data as Uint8Array ) when ( done = true ).
 */
function* decoder(
  sourceBase64ArrayBuffer, skipLineCount, progressToYield, progressToAdvance, suspendByteCount) {

  // 0. Initialize.

  let sourceByteLength = sourceBase64ArrayBuffer.byteLength;
  let sourceBytes = new Uint8Array( sourceBase64ArrayBuffer );

//!!! (2019/06/14 Remarked)
// //  suspendByteCount = Math.max(suspendByteCount | 0, Math.min(1024, sourceByteLength));
//
// //  suspendByteCount |= 0;  // Bitwising OR with zero is for converting to integer (if it is undefined or null).
//
//   // Ensure between [0, min(1024, sourceByteLength)].
//   //
//   // It is important that the suspendByteCount is not greater than source length. So the
//   // nextYieldAccumulation can be used for boundary checking.
// //!!!
// //  suspendByteCount = Math.max(0, Math.min(1024, sourceByteLength));
//   suspendByteCount = Math.max(0, Math.max(suspendByteCount | 0, Math.min(1024, sourceByteLength)));

  // Ensure between [0, min(1024, sourceByteLength)].
  //
  // It is important that the suspendByteCount is not greater than source length. So the
  // nextYieldAccumulation can be used as boundary checking to reduce checking times and increase performance.
  {
    // If undefined or null or negative or zero or less than 1, set to default.
    if ((suspendByteCount | 0) <= 0)
      suspendByteCount = 1024;
    if (suspendByteCount > sourceByteLength)
      suspendByteCount = sourceByteLength;
  }

  // Initialize progress.
  progressToAdvance.accumulation = 0;
  progressToAdvance.total = sourceByteLength;

//!!!
//  let yieldCount = 0;
  let nextYieldAccumulation = suspendByteCount;

  // 1. Skip specified lines.
  {
    let skippedLineCount = 0;

    while (progressToAdvance.accumulation < sourceByteLength) {
      if (skippedLineCount >= skipLineCount)
        break;                 // Already skip enough lines.

//!!!
      // (This inner loop combines both source and yield boundary checking. Reducing checking to increase performance.)
      while (progressToAdvance.accumulation < nextYieldAccumulation) {
        if (skippedLineCount >= skipLineCount)
          break;                 // Already skip enough lines.

        let rawByte = sourceBytes[ progressToAdvance.accumulation++ ];

        if (13 == rawByte) {     // "\r" (carriage return; CR)
          ++skippedLineCount;    // One line is skipped.

          // If a LF follows a CR, it is considered as CRLF sequence and viewed as the same one line.
          //
          // Note: It may exceed the nextYieldAccumulation boundary. But should not exceed sourceByteLength.
          if (   (progressToAdvance.accumulation < sourceByteLength)
              && (10 == sourceBytes[ progressToAdvance.accumulation ])
             ) {
            ++progressToAdvance.accumulation; // Skip it.
          }

        } else {
          if (10 == rawByte)    // "\n" (new line; LF)
            ++skippedLineCount; // One line is skipped.
        }
      }
// !!!
//       // Every suspendByteCount, release CPU time (and report progress).
//       if (progressToAdvance.accumulation >= nextYieldAccumulation) {
//         nextYieldAccumulation = progressToAdvance.accumulation + suspendByteCount;
//         yield progressToYield;
//       }

      // Every suspendByteCount, release CPU time (and report progress).
      if (progressToAdvance.accumulation >= nextYieldAccumulation) {
//!!!
//         nextYieldAccumulation = progressToAdvance.accumulation + suspendByteCount;
//
//         if (nextYieldAccumulation > sourceByteLength)
//           nextYieldAccumulation = sourceByteLength;

        nextYieldAccumulation = Math.min(sourceByteLength, progressToAdvance.accumulation + suspendByteCount);
//!!!
//        ++yieldCount;
        yield progressToYield;
      }
    }
  }

  // 2. Decode.

  // Decoding base64 will result in a shorter data (about 75% (= 3 / 4) in size).
  let possibleBase64ByteLength = (sourceByteLength - progressToAdvance.accumulation);  // Forget the skipped lines.
  let targetByteLength = Math.ceil(possibleBase64ByteLength * 0.75);
  let targetArrayBuffer = new ArrayBuffer( targetByteLength );
  let targetBytes = new Uint8Array( targetArrayBuffer );

  let resultByteCount = 0;  // Accumulate the real result byte count.

  {
    const BYTES_PER_DECODE_UNIT = 4; // A decode unit consists of 4 base64 encoded source bytes.
    let encodedBytes = new Array( BYTES_PER_DECODE_UNIT ); // Faster than using Uint8Array().

    while (progressToAdvance.accumulation < sourceByteLength) {

//!!!
      // (This inner loop combines both source and yield boundary checking. Reducing checking to increase performance.)
      while (progressToAdvance.accumulation < nextYieldAccumulation) {

        // Extract 4 source bytes.
        let j = 0;
        while (j < BYTES_PER_DECODE_UNIT) {

          // Note: It may exceed the nextYieldAccumulation boundary. But should not exceed sourceByteLength.
          if (progressToAdvance.accumulation >= sourceByteLength)
            break; // Decoding is done. (Ignore last non-4-bytes.)

          encodedBytes[ j ] = table_base64_Uint8_to_index[ sourceBytes[ progressToAdvance.accumulation++ ] ];

          if (255 === encodedBytes[ j ])
            continue; // Skip any non-base64 bytes.

          ++j;
        }

        if (j != BYTES_PER_DECODE_UNIT)
          break; // Decoding is done. (Ignore last non-4-bytes.)

        targetBytes[resultByteCount++] =  (encodedBytes[ 0 ]       << 2) | (encodedBytes[ 1 ] >> 4);
        targetBytes[resultByteCount++] = ((encodedBytes[ 1 ] & 15) << 4) | (encodedBytes[ 2 ] >> 2);
        targetBytes[resultByteCount++] = ((encodedBytes[ 2 ] &  3) << 6) | (encodedBytes[ 3 ] & 63);
      }

//!!!
//       // Every suspendByteCount, release CPU time (and report progress).
//       if (progressToAdvance.accumulation >= nextYieldAccumulation) {
//         nextYieldAccumulation = progressToAdvance.accumulation + suspendByteCount;
//         yield progressToYield;
//       }

      // Every suspendByteCount, release CPU time (and report progress).
      if (progressToAdvance.accumulation >= nextYieldAccumulation) {
//!!!
//         nextYieldAccumulation = progressToAdvance.accumulation + suspendByteCount;
//
//         if (nextYieldAccumulation > sourceByteLength)
//           nextYieldAccumulation = sourceByteLength;
        nextYieldAccumulation = Math.min(sourceByteLength, progressToAdvance.accumulation + suspendByteCount);
//!!!
//        ++yieldCount;
        yield progressToYield;
      }

    }
  }

  // 3. Result.

  // The resultBytes is a sub-range of target buffer.
  //
  // Because the source may have some non-base64 codes which will be ignored,
  // the result data may be less than target length.
  let resultBytes = new Uint8Array( targetArrayBuffer, 0, resultByteCount );

//!!! (2019/06/14) Remarked. not feasible.
//   if (   // Never yield (i.e. never report progress), report at least once for done.
//          (nextYieldAccumulation == suspendByteCount)
//
//          // Or, some advance has been made after last progress report, report for done.
//       || ((nextYieldAccumulation - progressToAdvance.accumulation) < suspendByteCount)
//      )
//     yield progressToYield; // Report the progress has been done (100%).
//   else
//     ; // The last progress report is (just luckily) 100%. No need to report the progress again.

//!!! (2019/06/14) Remarked. not workable.
//   if (   // Never yield (i.e. never report progress), report at least once (for done).
//          (0 == yieldCount)
// !!! wrong
//          // Or, some advance has been made after last progress report, report for done.
//       || ((nextYieldAccumulation - progressToAdvance.accumulation) < suspendByteCount)
//      )
//     yield progressToYield; // Report the progress has been done (100%).
//   else
//     ; // The last progress report is (just luckily) 100%. No need to report the progress again.

  yield progressToYield; // Report the progress has been done (100%).

  return resultBytes;
}
<|endoftext|>


<commit_before>'use strict';

angular.module('chatClientApp')
    .controller('MessagesCtrl', function ($scope, $rootScope, $timeout, Message, SelectedTopic, User, pollInterval) {
        var createdMessages = [];
        var pollTimeout;
        var messageQueryParams = {};

        $scope.user = User.getUser();
        $scope.topic = SelectedTopic.getSelectedTopic();

        if ($scope.topic) {
            messageQueryParams = {topic_id: $scope.topic.id};

            $scope.message = newMessage();
            $scope.messages = Message.query(messageQueryParams, function() {
                // Start polling messages after successfull initial load
                pollMessages();
            });
        }

        var pollMessages = function() {
            pollTimeout = $timeout(function() {
                Message.query(messageQueryParams, function(messages) {
                    $scope.messages = messages;
                    pollMessages();
                });
            }, pollInterval);
        };

        $rootScope.$on("$routeChangeStart", function() {
            // Stop polling when route changes
            $timeout.cancel(pollTimeout);
        });

        $scope.save = function(message) {
            if (!$scope.message.id) {
                createdMessages.push($scope.message);
            }

            $scope.message.$save();

            $scope.messages.push($scope.message);

            $scope.message = newMessage();
        }

        $scope.delete = function(message) {
            Message.delete(message, function() {
                _.remove($scope.messages, message);
            });
        }

        $scope.createdMessage = function(message) {
            var index = _.findIndex(createdMessages, {id: message.id});

            return index >= 0;
        }

        function newMessage() {
            var message = new Message();
            message._topic = {id: $scope.topic.id};

            if ($scope.user) {
                message.user = $scope.user.name;
            }

            return message;
        }
    });
<commit_msg>Post user email with message<commit_after>'use strict';

angular.module('chatClientApp')
    .controller('MessagesCtrl', function ($scope, $rootScope, $timeout, Message, SelectedTopic, User, pollInterval) {
        var createdMessages = [];
        var pollTimeout;
        var messageQueryParams = {};

        $scope.user = User.getUser();
        $scope.topic = SelectedTopic.getSelectedTopic();

        if ($scope.topic) {
            messageQueryParams = {topic_id: $scope.topic.id};

            $scope.message = newMessage();
            $scope.messages = Message.query(messageQueryParams, function() {
                // Start polling messages after successfull initial load
                pollMessages();
            });
        }

        var pollMessages = function() {
            pollTimeout = $timeout(function() {
                Message.query(messageQueryParams, function(messages) {
                    $scope.messages = messages;
                    pollMessages();
                });
            }, pollInterval);
        };

        $rootScope.$on("$routeChangeStart", function() {
            // Stop polling when route changes
            $timeout.cancel(pollTimeout);
        });

        $scope.save = function(message) {
            if (!$scope.message.id) {
                createdMessages.push($scope.message);
            }

            $scope.message.$save();

            $scope.messages.push($scope.message);

            $scope.message = newMessage();
        }

        $scope.delete = function(message) {
            Message.delete(message, function() {
                _.remove($scope.messages, message);
            });
        }

        $scope.createdMessage = function(message) {
            var index = _.findIndex(createdMessages, {id: message.id});

            return index >= 0;
        }

        function newMessage() {
            var message = new Message();
            message._topic = {id: $scope.topic.id};

            if ($scope.user) {
                message.user = $scope.user.name;
                message.email = $scope.user.email;
            }

            return message;
        }
    });
<|endoftext|>


<commit_before>'use strict';

/**
 * @ngdoc function
 * @name tagrefineryGuiApp.controller:WorkflowCtrl
 * @description
 * # WorkflowCtrl
 * Controller of the tagrefineryGuiApp
 */
angular.module('tagrefineryGuiApp')
  .controller('WorkflowCtrl', ["$scope", "socket", function ($scope, socket) {
    var that = this;

    // State variables
    that.init = false;
    that.initRunning = false;
    that.preRunning = false;
    that.spellRunning = false;
    that.compRunning = false;

    that.mode = "";

    $scope.showStep = false;
    $scope.currentStep = 0;

    ////////////////////////////////////////////////
    // Start
    ////////////////////////////////////////////////

    socket.on('selectedMode', function (data) {
      that.mode = data;

      that.init = true;
    });

    socket.on('initRunning', function (data) {
      that.initRunning = data == "started"
    });

    socket.on('computePre', function (data) {
      that.preRunning = data == "started"
    });

    socket.on('computeSpell', function (data) {
      that.spellRunning = data == "started"
    });

    socket.on('computeComp', function (data) {
      that.compRunning = data == "started"
    });

    // Choose value
    that.customize = function()
    {
      $scope.showStep = true;

      if($scope.currentStep == 1)
      {
        socket.emit("getBlacklist", "")
      }
      if($scope.currentStep == 2)
      {
        socket.emit("getWhitelist", "")
      }
    };

    that.ok = function()
    {
      $scope.showStep = false;
      $scope.currentStep++;

      if($scope.currentStep == 6)
      {
        $scope.showStep = true;
        $scope.$broadcast("guidedResult");
      }

      that.apply();
    };

    that.back = function()
    {
      $scope.showStep = false;
      $scope.currentStep--;
    }

    that.output = function()
    {
      $scope.$broadcast("guidedResult");
    };

    that.reset = function()
    {
      $scope.showStep = false;
      $scope.currentStep = 0;
    };

    that.advanced = function()
    {
      that.guided = false;
      that.free = true;

      socket.emit("selectMode", "free");
    };

    that.apply = function()
    {
      // Let the child apply changes
      $scope.$broadcast("apply");

      socket.emit("computeWorkflow", "");
    };

    that.getPercent = function()
    {
      return ($scope.currentStep / 6) * 100;
    }

  }]);
<commit_msg>Customize toggles now the additional information<commit_after>'use strict';

/**
 * @ngdoc function
 * @name tagrefineryGuiApp.controller:WorkflowCtrl
 * @description
 * # WorkflowCtrl
 * Controller of the tagrefineryGuiApp
 */
angular.module('tagrefineryGuiApp')
  .controller('WorkflowCtrl', ["$scope", "socket", function ($scope, socket) {
    var that = this;

    // State variables
    that.init = false;
    that.initRunning = false;
    that.preRunning = false;
    that.spellRunning = false;
    that.compRunning = false;

    that.mode = "";

    $scope.showStep = false;
    $scope.currentStep = 0;

    ////////////////////////////////////////////////
    // Start
    ////////////////////////////////////////////////

    socket.on('selectedMode', function (data) {
      that.mode = data;

      that.init = true;
    });

    socket.on('initRunning', function (data) {
      that.initRunning = data == "started"
    });

    socket.on('computePre', function (data) {
      that.preRunning = data == "started"
    });

    socket.on('computeSpell', function (data) {
      that.spellRunning = data == "started"
    });

    socket.on('computeComp', function (data) {
      that.compRunning = data == "started"
    });

    // Choose value
    that.customize = function()
    {
      $scope.showStep =! $scope.showStep;

      if($scope.currentStep == 1)
      {
        socket.emit("getBlacklist", "")
      }
      if($scope.currentStep == 2)
      {
        socket.emit("getWhitelist", "")
      }
    };

    that.ok = function()
    {
      $scope.showStep = false;
      $scope.currentStep++;

      if($scope.currentStep == 6)
      {
        $scope.showStep = true;
        $scope.$broadcast("guidedResult");
      }

      that.apply();
    };

    that.back = function()
    {
      $scope.showStep = false;
      $scope.currentStep--;
    }

    that.output = function()
    {
      $scope.$broadcast("guidedResult");
    };

    that.reset = function()
    {
      $scope.showStep = false;
      $scope.currentStep = 0;
    };

    that.advanced = function()
    {
      that.guided = false;
      that.free = true;

      socket.emit("selectMode", "free");
    };

    that.apply = function()
    {
      // Let the child apply changes
      $scope.$broadcast("apply");

      socket.emit("computeWorkflow", "");
    };

    that.getPercent = function()
    {
      return ($scope.currentStep / 6) * 100;
    }

  }]);
<|endoftext|>


<commit_before>'use strict';

/**
 * @ngdoc directive
 * @name midjaApp.directive:cartodbMap
 * @description
 * # cartodbMap
 */
angular.module('midjaApp')
  .directive('regionMap', function(L, $http, $rootScope, $q, dataService,
    $uibModal) {

    // tileserver-gl-light URL
    var tileserverBaseUrl = "https://tiles.map.midja.org";
    var tileJsonUrlTmpl = _.template(
      tileserverBaseUrl + "/data/<%=regionType%>.json");
    var defaultStyles = {
      'regions': _.constant({
        weight: 1,
        color: '#000000',
        fillColor: '#ffffff',
        opacity: 1,
        fillOpacity: 0.7,
        fill: true
      }),
      'points': _.constant({
        weight: 0.1,
        color: '#000000',
        radius: 3,
        opacity: 0
      })
    };
    var regionHeadingTmpl = _.template("<%=name%> (<%=code%>)");

    // Modified VectorGrid which can take a layer order, so our points can be
    // consistently rendered above regions.
    var ModifiedVectorGrid = L.VectorGrid.Protobuf.extend({
      _getVectorTilePromise: function(coords) {
        var layerOrder = this.options.layerOrder || [];
        var overriddenMethod = _.bind(
          L.VectorGrid.Protobuf.prototype._getVectorTilePromise,
          this);
        return overriddenMethod(coords)
          .then(function(data) {
            var sortedKeys = _.sortBy(_.keys(data.layers), function(
              layer) {
              return layerOrder.indexOf(layer);
            });
            data.layers = _.zipObject(
              sortedKeys,
              _.map(sortedKeys, _.propertyOf(data.layers))
            );
            return data;
          });
      }
    })

    // Generate random ID for map
    var mapId = 'map-' + Math.random().toString().slice(2);
    // Create template using ID
    var template = _.template(
      '<div id="<%=id%>" class="region-map">
'
    )({
      id: mapId
    });

    var map = null;
    var regionLayers = null;

    var scopeAttributes = {
      'regionType': '=',
      'regions': '=',
      'choroplethTopic': '=',
      'bubblesTopic': '=',
      'choroplethVisible': '=',
      'bubblesVisible': '='
    }

    return {
      template: template,
      restrict: 'E',
      scope: scopeAttributes,
      replace: true,
      link: postLink
    };

    function setupEvents(scope) {
      function changeEmitter(baseEventName) {
        return function(newVal, oldVal) {
          if (!_.isEqual(newVal, oldVal)) {
            console.log(baseEventName + ':change', newVal, oldVal);
            scope.$emit(baseEventName + ':change', newVal, oldVal);
          }
        }
      }
      // Setup attribute watchers
      for (var attr in scopeAttributes) {
        scope.$watch(attr, changeEmitter(_.kebabCase(attr)));
      }
    }

    function getTileJSON(regionType) {
      return $http.get(tileJsonUrlTmpl({
          regionType: regionType
        }))
        .then(function(response) {
          return response.data;
        });
    }

    function featureStyleCreator(scope, layer) {
      return function(properties, zoom) {
        var hideStyle = _.constant({
          opacity: 0,
          fillOpacity: 0
        });
        var regionCode = properties.region_code
        var region = _.find(
          scope.regions,
          _.flow(
            _.property('code'),
            _.partial(_.isEqual, regionCode)));
        if (!region) {
          return hideStyle();
        }
        return (
          scope.styles[layer] || defaultStyles[layer] || hideStyle
        )(region);
      };
    }

    function setupHooks(scope) {
      var redrawVectorGrid = function redrawVectorGrid() {
        if (scope.vectorGrid) {
          scope.vectorGrid.redraw();
        }
      };
      scope.$on('region-type:change', function _regionTypeChange(evt, regionType) {
        // Create new VectorGrid
        getTileJSON(regionType).then(function(metadata) {
          var styles = _.zipObject(_.map(
            metadata.vector_layers,
            _.flow(
              _.property('id'),
              function(layerId) {
                return [layerId, featureStyleCreator(scope,
                  layerId)];
              }
            )));
          var bounds = L.latLngBounds(_.map(
            _.chunk(metadata.bounds, 2),
            function(p) {
              return L.latLng(p[1], p[0]);
            }));
          var vectorGrid = new ModifiedVectorGrid(metadata.tiles[0], {
            attribution: metadata.attribution,
            bounds: bounds,
            interactive: true,
            layerOrder: ['regions', 'points'],
            rendererFactory: L.canvas.tile,
            vectorTileLayerStyles: styles
          });
          var resolveRegion = function (regionCode) {
            return _.find(
              scope.regions,
              _.matchesProperty('code', regionCode));
          }
          vectorGrid.on('mouseover', function(evt) {
            var region = resolveRegion(evt.layer.properties.region_code);
            if (region) {
              var data =
                scope.regionData && scope.regionData[region.code];
              scope.$emit('vector-grid:mouseover', region, data);
            }
          });
          vectorGrid.on('click', function(evt) {
            var region = resolveRegion(evt.layer.properties.region_code);
            if (region) {
              var data =
                scope.regionData && scope.regionData[region.code];
              scope.$emit('vector-grid:click', region, data);
            }
          });

          // Trigger replacement
          scope.styles = {};
          var previousVectorGrid = scope.vectorGrid;
          scope.vectorGrid = vectorGrid;
          scope.$emit('vector-grid:change', vectorGrid, previousVectorGrid);
        });
      });
      scope.$on('regions:change', redrawVectorGrid);
      var refreshData = function _refreshData(evt) {
        return $q(function(resolve) {
          var attributes = _.chain([scope.choroplethTopic, scope.bubblesTopic])
            .filter(_.isObject)
            .map(_.property('name'))
            .value();
          if (_.isEmpty(attributes)) {
            scope.regionData = {};
            resolve(scope.regionData);
          } else {
            dataService.getAttributesForRegions(
              scope.regionType, attributes, scope.regions
            ).then(function(data) {
              scope.regionData = data;
              resolve(scope.regionData);
            });
          }
        });
      }
      var updateChoropleth = function _updateChoropleth(evt) {
        var regionData = scope.regionData;
        var choroplethTopic = scope.choroplethTopic;
        if (_.isObject(choroplethTopic) && scope.choroplethVisible) {
          var buckets = generateBuckets(
            _.map(
              _.values(regionData),
              _.property(choroplethTopic.name)));
          var colorer = colorerForBuckets("cb-YlOrRd", buckets);
          var regionColor = function(region) {
            try {
              return colorer(regionData[region.code][choroplethTopic.name]);
            } catch (e) {
              return null;
            }
          };
          scope.$emit('legend:set',
            'regions', 'right', getLegend(buckets, colorer));
          scope.styles = _.defaults({
            regions: function(region) {
              return _.defaults({
                fillColor: regionColor(region)
              }, defaultStyles['regions'](region));
            }
          }, scope.styles);
        } else {
          scope.$emit('legend:clear', 'regions');
          scope.styles = _.defaults({
            regions: null
          }, scope.styles);
        }
      };
      var updateBubbles = function _updateBubbles(evt) {
        var regionData = scope.regionData;
        var bubblesTopic = scope.bubblesTopic;
        if (_.isObject(bubblesTopic) && scope.bubblesVisible) {
          var buckets = generateBuckets(
            _.map(
              _.values(regionData),
              _.property(bubblesTopic.name)));
          var colorer = colorerForBuckets("cb-Blues", buckets);
          var regionColor = function(region) {
            try {
              return colorer(regionData[region.code][bubblesTopic.name]);
            } catch (e) {
              return null;
            }
          };
          scope.styles = _.defaults({
            points: function(region) {
              return _.defaults({
                fill: true,
                fillColor: regionColor(region),
                fillOpacity: 1,
                opacity: 1
              }, defaultStyles['points'](region));
            }
          }, scope.styles);
          scope.$emit('legend:set',
            'points', 'left', getLegend(buckets, colorer));
        } else {
          scope.styles = _.defaults({
            points: null
          }, scope.styles);
          scope.$emit('legend:clear', 'points');
        }
      };
      var redraw = _.debounce(function() {
        refreshData()
          .then(updateChoropleth)
          .then(updateBubbles)
          .then(redrawVectorGrid);
      }, 50);
      scope.$on('choropleth-topic:change', redraw);
      scope.$on('choropleth-visible:change', redraw);
      scope.$on('bubbles-topic:change', redraw);
      scope.$on('bubbles-visible:change', redraw);
      scope.$on('region-data:change', redraw);
      scope.$on('choropleth-visible:change', redraw);
      scope.$on('bubbles-visible:change', redraw);
    }

    function generateBuckets(values) {
      var series = _.filter(values, function(v) {
        return _.isNumber(v) && !_.isNaN(v);
      });
      var maxBuckets = Math.min(5, _.uniq(series).length)
      if (maxBuckets == 0) {
        return [];
      } else {
        return dataService.getCkmeansBuckets(series, maxBuckets);
      }
    }

    function colorerForBuckets(colorPaletteName, buckets) {
      var colors = palette(colorPaletteName, buckets.length);
      return function(v) {
        var i = _.findIndex(buckets, function(bucket) {
          return bucket.contains(v);
        });
        return i == -1 ? null : '#' + colors[i];
      };
    }

    function getLegend(buckets, colorer) {
      var div = L.DomUtil.create('div', 'legend');
      var ul = L.DomUtil.create('ul', '', div);
      buckets.forEach(function(bucket) {
        var li = L.DomUtil.create('li', '', ul);
        var bullet = L.DomUtil.create('div', 'bullet', li);
        angular.element(bullet).css("background", colorer(bucket.min));
        var text = L.DomUtil.create('span', '', li);
        text.innerHTML = _.map(
          [bucket.min, bucket.max],
          dataService.formatNumber
        ).join(" - ");
      });
      return div;
    }

    function postLink(scope, element, attrs) {
      setupEvents(scope);
      setupHooks(scope);

      var map = L.map(mapId, {
        center: [-27, 134],
        zoom: 4
      });

      // add a nice baselayer from Stamen
      L.tileLayer(
        'https://maps.nlp.nokia.com/maptiler/v2/maptile/newest/normal.day/{z}/{x}/{y}/' +
        '256/png8?lg=eng&token=A7tBPacePg9Mj_zghvKt9Q&app_id=KuYppsdXZznpffJsKT24', {
          attribution: 'Stamen'
        }).addTo(map);

      var topicAttribution = L.control.attribution({
        prefix: false,
        position: 'bottomleft'
      })
      topicAttribution.onAdd = function() {
        this._container =
          L.DomUtil.create('div', 'leaflet-control-attribution');
	      L.DomEvent.disableClickPropagation(this._container);
        this._update();
        return this._container;
      };
      topicAttribution.addTo(map);
      var topicAttributionHandler = function(e, newTopic, oldTopic) {
        if (oldTopic && oldTopic.source) {
          topicAttribution.removeAttribution(oldTopic.source.name);
        }
        if (newTopic && newTopic.source) {
          topicAttribution.addAttribution(newTopic.source.name);
        }
      }
      scope.$on('choropleth-topic:change', topicAttributionHandler);
      scope.$on('bubbles-topic:change', topicAttributionHandler);

      scope.$on('vector-grid:change', function(evt, newLayer, oldLayer) {
        if (oldLayer) {
          try {
            oldLayer.removeFrom(map);
          } catch (e) {}
        }
        if (newLayer) {
          newLayer.addTo(map);
        }
      });

      var featureControl = null;
      scope.$on('vector-grid:mouseover', function(e, region, data) {
        var newFeatureControl = L.control({
          position: 'topright'
        });
        newFeatureControl.onAdd = function(map) {
          var div = L.DomUtil.create('div', 'feature-hover');
          var heading = L.DomUtil.create('h1', '', div);
          heading.innerHTML = regionHeadingTmpl(region);
          var topics = _.chain([scope.choroplethTopic, scope.bubblesTopic])
            .filter(_.isObject)
            .uniq(_.property('name'))
            .filter(_.flow(_.property('name'), _.partial(_.has, data)))
            .value();
          if (!_.isEmpty(topics)) {
            var dl = L.DomUtil.create('dl', '', div);
            _.forEach(topics, function(topic) {
              var dt = L.DomUtil.create('dt', '', dl);
              dt.innerHTML=topic.description;
              var dd = L.DomUtil.create('dd', '', dl);
              dd.innerHTML=dataService.formatNumber(data[topic.name]);
            })
          }
          return div;
        };
        if (featureControl) {
          featureControl.remove();
        }
        featureControl = newFeatureControl;
        featureControl.addTo(map);
      });

      scope.$on('vector-grid:click', _.debounce(function(e, region) {
        var vm = $rootScope.$$childTail.vm;
        var regionType = region.type;
        var regionCodeAttribute = regionType+'_code';
        var attributes = vm.vis.topics;
        var attrNames = _.map(vm.vis.topics, _.property('name'));

        dataService.getAttributesForRegions(regionType, attrNames, [{
          'code': region.code
        }]).then(function(data) {
          console.log(data);
          var modalInstance = $uibModal.open({
            animation: true,
            templateUrl: 'details.html',
            controller: 'DetailsModalInstanceCtrl',
            resolve: {
              context: {
                heading: regionHeadingTmpl(region),
                attributes: attributes,
                getValue: _.propertyOf(
                    _.first(_.values(data)))
              }
            }
          });
        });
      }, 100));

      var legends = {};
      scope.$on('legend:set', function(evt, type, position, legendEl) {
        var legend = L.control({
          position: 'bottom'+position
        });
        legend.onAdd = _.constant(legendEl);
        if (legends[type]) {
          legends[type].remove();
        }
        legends[type] = legend;
        legend.addTo(map);
      });
      scope.$on('legend:clear', function(evt, type) {
        if (legends[type]) {
          legends[type].remove();
        }
      });

      scope.$emit('region-type:change', scope.regionType);
    }

  });
<commit_msg>Handle missing data when region selection expands<commit_after>'use strict';

/**
 * @ngdoc directive
 * @name midjaApp.directive:cartodbMap
 * @description
 * # cartodbMap
 */
angular.module('midjaApp')
  .directive('regionMap', function(L, $http, $rootScope, $q, dataService,
    $uibModal) {

    // tileserver-gl-light URL
    var tileserverBaseUrl = "https://tiles.map.midja.org";
    var tileJsonUrlTmpl = _.template(
      tileserverBaseUrl + "/data/<%=regionType%>.json");
    var defaultStyles = {
      'regions': _.constant({
        weight: 1,
        color: '#000000',
        fillColor: '#ffffff',
        opacity: 1,
        fillOpacity: 0.7,
        fill: true
      }),
      'points': _.constant({
        weight: 0.1,
        color: '#000000',
        radius: 3,
        opacity: 0
      })
    };
    var regionHeadingTmpl = _.template("<%=name%> (<%=code%>)");

    // Modified VectorGrid which can take a layer order, so our points can be
    // consistently rendered above regions.
    var ModifiedVectorGrid = L.VectorGrid.Protobuf.extend({
      _getVectorTilePromise: function(coords) {
        var layerOrder = this.options.layerOrder || [];
        var overriddenMethod = _.bind(
          L.VectorGrid.Protobuf.prototype._getVectorTilePromise,
          this);
        return overriddenMethod(coords)
          .then(function(data) {
            var sortedKeys = _.sortBy(_.keys(data.layers), function(
              layer) {
              return layerOrder.indexOf(layer);
            });
            data.layers = _.zipObject(
              sortedKeys,
              _.map(sortedKeys, _.propertyOf(data.layers))
            );
            return data;
          });
      }
    })

    // Generate random ID for map
    var mapId = 'map-' + Math.random().toString().slice(2);
    // Create template using ID
    var template = _.template(
      '<div id="<%=id%>" class="region-map">
'
    )({
      id: mapId
    });

    var map = null;
    var regionLayers = null;

    var scopeAttributes = {
      'regionType': '=',
      'regions': '=',
      'choroplethTopic': '=',
      'bubblesTopic': '=',
      'choroplethVisible': '=',
      'bubblesVisible': '='
    }

    return {
      template: template,
      restrict: 'E',
      scope: scopeAttributes,
      replace: true,
      link: postLink
    };

    function setupEvents(scope) {
      function changeEmitter(baseEventName) {
        return function(newVal, oldVal) {
          if (!_.isEqual(newVal, oldVal)) {
            console.log(baseEventName + ':change', newVal, oldVal);
            scope.$emit(baseEventName + ':change', newVal, oldVal);
          }
        }
      }
      // Setup attribute watchers
      for (var attr in scopeAttributes) {
        scope.$watch(attr, changeEmitter(_.kebabCase(attr)));
      }
    }

    function getTileJSON(regionType) {
      return $http.get(tileJsonUrlTmpl({
          regionType: regionType
        }))
        .then(function(response) {
          return response.data;
        });
    }

    function featureStyleCreator(scope, layer) {
      return function(properties, zoom) {
        var hideStyle = _.constant({
          opacity: 0,
          fillOpacity: 0
        });
        var regionCode = properties.region_code
        var region = _.find(
          scope.regions,
          _.flow(
            _.property('code'),
            _.partial(_.isEqual, regionCode)));
        if (!region) {
          return hideStyle();
        }
        return (
          scope.styles[layer] || defaultStyles[layer] || hideStyle
        )(region);
      };
    }

    function setupHooks(scope) {
      var redrawVectorGrid = function redrawVectorGrid() {
        if (scope.vectorGrid) {
          scope.vectorGrid.redraw();
        }
      };
      scope.$on('region-type:change', function _regionTypeChange(evt, regionType) {
        // Create new VectorGrid
        getTileJSON(regionType).then(function(metadata) {
          var styles = _.zipObject(_.map(
            metadata.vector_layers,
            _.flow(
              _.property('id'),
              function(layerId) {
                return [layerId, featureStyleCreator(scope,
                  layerId)];
              }
            )));
          var bounds = L.latLngBounds(_.map(
            _.chunk(metadata.bounds, 2),
            function(p) {
              return L.latLng(p[1], p[0]);
            }));
          var vectorGrid = new ModifiedVectorGrid(metadata.tiles[0], {
            attribution: metadata.attribution,
            bounds: bounds,
            interactive: true,
            layerOrder: ['regions', 'points'],
            rendererFactory: L.canvas.tile,
            vectorTileLayerStyles: styles
          });
          var resolveRegion = function (regionCode) {
            return _.find(
              scope.regions,
              _.matchesProperty('code', regionCode));
          }
          vectorGrid.on('mouseover', function(evt) {
            var region = resolveRegion(evt.layer.properties.region_code);
            if (region) {
              var data =
                scope.regionData && scope.regionData[region.code];
              scope.$emit('vector-grid:mouseover', region, data);
            }
          });
          vectorGrid.on('click', function(evt) {
            var region = resolveRegion(evt.layer.properties.region_code);
            if (region) {
              var data =
                scope.regionData && scope.regionData[region.code];
              scope.$emit('vector-grid:click', region, data);
            }
          });

          // Trigger replacement
          scope.styles = {};
          var previousVectorGrid = scope.vectorGrid;
          scope.vectorGrid = vectorGrid;
          scope.$emit('vector-grid:change', vectorGrid, previousVectorGrid);
        });
      });
      var refreshData = function _refreshData(evt) {
        return $q(function(resolve) {
          var attributes = _.chain([scope.choroplethTopic, scope.bubblesTopic])
            .filter(_.isObject)
            .map(_.property('name'))
            .value();
          if (_.isEmpty(attributes)) {
            scope.regionData = {};
            resolve(scope.regionData);
          } else {
            dataService.getAttributesForRegions(
              scope.regionType, attributes, scope.regions
            ).then(function(data) {
              scope.regionData = data;
              resolve(scope.regionData);
            });
          }
        });
      }
      var updateChoropleth = function _updateChoropleth(evt) {
        var regionData = scope.regionData;
        var choroplethTopic = scope.choroplethTopic;
        if (_.isObject(choroplethTopic) && scope.choroplethVisible) {
          var buckets = generateBuckets(
            _.map(
              _.values(regionData),
              _.property(choroplethTopic.name)));
          var colorer = colorerForBuckets("cb-YlOrRd", buckets);
          var regionColor = function(region) {
            try {
              return colorer(regionData[region.code][choroplethTopic.name]);
            } catch (e) {
              return null;
            }
          };
          scope.$emit('legend:set',
            'regions', 'right', getLegend(buckets, colorer));
          scope.styles = _.defaults({
            regions: function(region) {
              return _.defaults({
                fillColor: regionColor(region)
              }, defaultStyles['regions'](region));
            }
          }, scope.styles);
        } else {
          scope.$emit('legend:clear', 'regions');
          scope.styles = _.defaults({
            regions: null
          }, scope.styles);
        }
      };
      var updateBubbles = function _updateBubbles(evt) {
        var regionData = scope.regionData;
        var bubblesTopic = scope.bubblesTopic;
        if (_.isObject(bubblesTopic) && scope.bubblesVisible) {
          var buckets = generateBuckets(
            _.map(
              _.values(regionData),
              _.property(bubblesTopic.name)));
          var colorer = colorerForBuckets("cb-Blues", buckets);
          var regionColor = function(region) {
            try {
              return colorer(regionData[region.code][bubblesTopic.name]);
            } catch (e) {
              return null;
            }
          };
          scope.styles = _.defaults({
            points: function(region) {
              return _.defaults({
                fill: true,
                fillColor: regionColor(region),
                fillOpacity: 1,
                opacity: 1
              }, defaultStyles['points'](region));
            }
          }, scope.styles);
          scope.$emit('legend:set',
            'points', 'left', getLegend(buckets, colorer));
        } else {
          scope.styles = _.defaults({
            points: null
          }, scope.styles);
          scope.$emit('legend:clear', 'points');
        }
      };
      var redraw = _.debounce(function() {
        refreshData()
          .then(updateChoropleth)
          .then(updateBubbles)
          .then(redrawVectorGrid);
      }, 50);
      scope.$on('regions:change', redraw);
      scope.$on('choropleth-topic:change', redraw);
      scope.$on('choropleth-visible:change', redraw);
      scope.$on('bubbles-topic:change', redraw);
      scope.$on('bubbles-visible:change', redraw);
      scope.$on('region-data:change', redraw);
      scope.$on('choropleth-visible:change', redraw);
      scope.$on('bubbles-visible:change', redraw);
    }

    function generateBuckets(values) {
      var series = _.filter(values, function(v) {
        return _.isNumber(v) && !_.isNaN(v);
      });
      var maxBuckets = Math.min(5, _.uniq(series).length)
      if (maxBuckets == 0) {
        return [];
      } else {
        return dataService.getCkmeansBuckets(series, maxBuckets);
      }
    }

    function colorerForBuckets(colorPaletteName, buckets) {
      var colors = palette(colorPaletteName, buckets.length);
      return function(v) {
        var i = _.findIndex(buckets, function(bucket) {
          return bucket.contains(v);
        });
        return i == -1 ? null : '#' + colors[i];
      };
    }

    function getLegend(buckets, colorer) {
      var div = L.DomUtil.create('div', 'legend');
      var ul = L.DomUtil.create('ul', '', div);
      buckets.forEach(function(bucket) {
        var li = L.DomUtil.create('li', '', ul);
        var bullet = L.DomUtil.create('div', 'bullet', li);
        angular.element(bullet).css("background", colorer(bucket.min));
        var text = L.DomUtil.create('span', '', li);
        text.innerHTML = _.map(
          [bucket.min, bucket.max],
          dataService.formatNumber
        ).join(" - ");
      });
      return div;
    }

    function postLink(scope, element, attrs) {
      setupEvents(scope);
      setupHooks(scope);

      var map = L.map(mapId, {
        center: [-27, 134],
        zoom: 4
      });

      // add a nice baselayer from Stamen
      L.tileLayer(
        'https://maps.nlp.nokia.com/maptiler/v2/maptile/newest/normal.day/{z}/{x}/{y}/' +
        '256/png8?lg=eng&token=A7tBPacePg9Mj_zghvKt9Q&app_id=KuYppsdXZznpffJsKT24', {
          attribution: 'Stamen'
        }).addTo(map);

      var topicAttribution = L.control.attribution({
        prefix: false,
        position: 'bottomleft'
      })
      topicAttribution.onAdd = function() {
        this._container =
          L.DomUtil.create('div', 'leaflet-control-attribution');
	      L.DomEvent.disableClickPropagation(this._container);
        this._update();
        return this._container;
      };
      topicAttribution.addTo(map);
      var topicAttributionHandler = function(e, newTopic, oldTopic) {
        if (oldTopic && oldTopic.source) {
          topicAttribution.removeAttribution(oldTopic.source.name);
        }
        if (newTopic && newTopic.source) {
          topicAttribution.addAttribution(newTopic.source.name);
        }
      }
      scope.$on('choropleth-topic:change', topicAttributionHandler);
      scope.$on('bubbles-topic:change', topicAttributionHandler);

      scope.$on('vector-grid:change', function(evt, newLayer, oldLayer) {
        if (oldLayer) {
          try {
            oldLayer.removeFrom(map);
          } catch (e) {}
        }
        if (newLayer) {
          newLayer.addTo(map);
        }
      });

      var featureControl = null;
      scope.$on('vector-grid:mouseover', function(e, region, data) {
        var newFeatureControl = L.control({
          position: 'topright'
        });
        newFeatureControl.onAdd = function(map) {
          var div = L.DomUtil.create('div', 'feature-hover');
          var heading = L.DomUtil.create('h1', '', div);
          heading.innerHTML = regionHeadingTmpl(region);
          var topics = _.chain([scope.choroplethTopic, scope.bubblesTopic])
            .filter(_.isObject)
            .uniq(_.property('name'))
            .filter(_.flow(_.property('name'), _.partial(_.has, data)))
            .value();
          if (!_.isEmpty(topics)) {
            var dl = L.DomUtil.create('dl', '', div);
            _.forEach(topics, function(topic) {
              var dt = L.DomUtil.create('dt', '', dl);
              dt.innerHTML=topic.description;
              var dd = L.DomUtil.create('dd', '', dl);
              dd.innerHTML=dataService.formatNumber(data[topic.name]);
            })
          }
          return div;
        };
        if (featureControl) {
          featureControl.remove();
        }
        featureControl = newFeatureControl;
        featureControl.addTo(map);
      });

      scope.$on('vector-grid:click', _.debounce(function(e, region) {
        var vm = $rootScope.$$childTail.vm;
        var regionType = region.type;
        var regionCodeAttribute = regionType+'_code';
        var attributes = vm.vis.topics;
        var attrNames = _.map(vm.vis.topics, _.property('name'));

        dataService.getAttributesForRegions(regionType, attrNames, [{
          'code': region.code
        }]).then(function(data) {
          console.log(data);
          var modalInstance = $uibModal.open({
            animation: true,
            templateUrl: 'details.html',
            controller: 'DetailsModalInstanceCtrl',
            resolve: {
              context: {
                heading: regionHeadingTmpl(region),
                attributes: attributes,
                getValue: _.propertyOf(
                    _.first(_.values(data)))
              }
            }
          });
        });
      }, 100));

      var legends = {};
      scope.$on('legend:set', function(evt, type, position, legendEl) {
        var legend = L.control({
          position: 'bottom'+position
        });
        legend.onAdd = _.constant(legendEl);
        if (legends[type]) {
          legends[type].remove();
        }
        legends[type] = legend;
        legend.addTo(map);
      });
      scope.$on('legend:clear', function(evt, type) {
        if (legends[type]) {
          legends[type].remove();
        }
      });

      scope.$emit('region-type:change', scope.regionType);
    }

  });
<|endoftext|>


<commit_before>const x = tf.randomNormal([2000, 2000, 4]);
//const b = tf.randomNormal([2000, 2000, 4]);
//const b = tf.randomNormal([2000, 2000, 1]);
const b = tf.randomNormal([1, 1, 4]);

function timesFunc( f ) {
  let times = 100; //10 * 1000;
  let y;
  for ( let i = 0; i < times; ++i ) {
    y = f();
    y.dispose();
  }
}

let testFuncArray = [
  tf.sin,
  tf.asin,
  tf.cos,
  tf.acos,
  tf.tan,
  tf.tanh,
  tf.sigmoid,
  tf.relu,
  tf.relu6,
  tf.log,
  tf.log1p,
  tf.exp,
  tf.expm1,
  tf.add,
  tf.softplus,
  tf.reciprocal,
];

async function testByBackend( backendName ) {
  console.log( `backend = ${backendName}` );
  await tf.setBackend( backendName );

  for ( let i = 0; i < testFuncArray.length; ++i ) {
    let testFunc = testFuncArray[ i ];
    let testFuncBind = testFunc.bind( null, x, b );
    let testFuncTimes = timesFunc.bind( null, testFuncBind );

    const time = await tf.time( testFuncTimes );
    console.log( `${testFunc.name}, kernelMs: ${time.kernelMs}, wallTimeMs: ${time.wallMs}`);
  }
}

//await testByBackend( "cpu" );
await testByBackend( "webgl" );
<commit_msg>Update console_test_func_perf.js<commit_after>const x = tf.randomNormal([2000, 2000, 4]);
//const b = tf.randomNormal([2000, 2000, 4]);
//const b = tf.randomNormal([2000, 2000, 1]);
const b = tf.randomNormal([1, 1, 4]);

function timesFunc( f ) {
  let times = 100; //10 * 1000;
  let y;
  for ( let i = 0; i < times; ++i ) {
    y = f();
    y.dispose();
  }
}

let testFuncArray = [
  tf.sin,
//  tf.asin,
  tf.cos,
//  tf.acos,
//  tf.tan,
  tf.tanh,
  tf.sigmoid,
  tf.relu,
  tf.relu6,
  tf.log,
  tf.log1p,
  tf.exp,
  tf.expm1,
  tf.softplus,
  tf.reciprocal,
  tf.add,
];

async function testByBackend( backendName ) {
  console.log( `backend = ${backendName}` );
  await tf.setBackend( backendName );

  for ( let i = 0; i < testFuncArray.length; ++i ) {
    let testFunc = testFuncArray[ i ];
    let testFuncBind = testFunc.bind( null, x, b );
    let testFuncTimes = timesFunc.bind( null, testFuncBind );

    const time = await tf.time( testFuncTimes );
    console.log( `${testFunc.name}, kernelMs: ${time.kernelMs}, wallTimeMs: ${time.wallMs}`);
  }
}

//await testByBackend( "cpu" );
await testByBackend( "webgl" );
<|endoftext|>