<commit_before>/* Copyright 2013-2014 Extended Mind Technologies Oy
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
 'use strict';

 function TasksController($rootScope, $scope, $timeout,
                          AnalyticsService, ArrayService, DateService, ItemsService, SwiperService,
                          TasksService, UISessionService) {

  // INITIALIZING
  if (angular.isFunction($scope.registerArrayChangeCallback))
    $scope.registerArrayChangeCallback('tasks', 'active', invalidateTasksArraysWithActiveTasks,
                                       'TasksController');

  var cachedTasksArrays = {};

  /*
  * Invalidate cached active tasks arrays.
  *
  * @param {Object} task Changed task.
  */
  function invalidateTasksArraysWithActiveTasks(tasks, modifiedTask, ownerUUID) {
    if (cachedTasksArrays[ownerUUID]) {
      for (var arrayType in cachedTasksArrays[ownerUUID]) {
        if (cachedTasksArrays[ownerUUID].hasOwnProperty(arrayType)) {
          // Every cached tasks array has cached tasks.
          if (arrayType === 'all') {
            if ($scope.getActiveFeature() === 'tasks')
              updateAllTasks(cachedTasksArrays[ownerUUID], ownerUUID);
            else
              cachedTasksArrays[ownerUUID]['all'] = undefined;
          }
          else if (arrayType === 'date') {
            var focusActive = $scope.getActiveFeature() === 'focus';

            for (var date in cachedTasksArrays[ownerUUID]['date']) {
              if (cachedTasksArrays[ownerUUID]['date'].hasOwnProperty(date)) {
                if (focusActive)
                  updateDateTasks(ownerUUID, cachedTasksArrays[ownerUUID]['date'], {date: date});
                else
                  delete cachedTasksArrays[ownerUUID]['date'][date];
              }
            }
          }
        }
      }
    }
  }

  function updateAllTasks(cachedTasks, ownerUUID) {

    function compareWithFrozenModified(a, b) {
      return $scope.getTaskModified(a) - $scope.getTaskModified(b);
    }

    // Get tasks.
    var allActiveTasks = TasksService.getTasks(ownerUUID);
    cachedTasks['all'] = [];
    // Sort and cache array.
    for (var i = 0; i < allActiveTasks.length; i++) {
      ArrayService.insertItemToArray(allActiveTasks[i],
                                     cachedTasks['all'],
                                     compareWithFrozenModified,
                                     true);
    }
  }

  /*
  * Show completed on a past date.
  */
  function updatePastDateTasks(allActiveTasks, pastDateTasksArray, pastDateYYYYMMDD) {
    var pastDateMidnight = pastDateYYYYMMDD.yyyymmddToNoonDate().setHours(0, 0, 0, 0);

    for (var i = allActiveTasks.length - 1; i >= 0; i--) {
      var task = allActiveTasks[i];
      if (task.trans.completed && new Date(task.trans.completed).setHours(0, 0, 0, 0) === pastDateMidnight)
        pastDateTasksArray.push(task);
    }
  }

  /*
  * Show tasks without a date.
  */
  function updateNoDateTasks(allActiveTasks, noDateTasksArray) {
    for (var i = 0; i < allActiveTasks.length; i++) {
      if (!allActiveTasks[i].trans.due) noDateTasksArray.push(allActiveTasks[i]);
    }
  }

  /*
  * Show overdue and due tasks and completed on date.
  */
  function updateTodayTasks(allActiveTasks, todayTasks, todayYYYYMMDD) {
    var todayMidnight = DateService.getTodayDateWithoutTime();

    for (var i = allActiveTasks.length - 1; i >= 0; i--) {
      var task = allActiveTasks[i];
      if (task.trans.due && task.trans.due <= todayYYYYMMDD &&
          !(task.trans.completed && task.trans.completed < todayMidnight))
      {
        todayTasks.push(task);
      }
      else if (task.trans.completed && task.trans.completed > todayMidnight) {
        todayTasks.push(task);
      }
    }
  }

  /*
  * Show due tasks and completed on date.
  */
  function updateFutureDateTasks(allActiveTasks, futureDateTasks, futureYYYYMMDD) {
    var futureDateMidnight = futureYYYYMMDD.yyyymmddToNoonDate().setHours(0, 0, 0, 0);

    for (var i = allActiveTasks.length - 1; i >= 0; i--) {
      var task = allActiveTasks[i];
      if (task.trans.due && task.trans.due === futureYYYYMMDD) {
        futureDateTasks.push(task);
      } else if (task.trans.completed &&
                 new Date(task.trans.completed).setHours(0, 0, 0, 0) === futureDateMidnight)
      {
        futureDateTasks.push(task);
      }
    }
  }

  function updateDateTasks(ownerUUID, cachedDates, info) {
    var pastDate;

    if (cachedDates[info.date])
      pastDate = cachedDates[info.date].pastDate;
    else if (info.pastDate)
      pastDate = info.pastDate;

    cachedDates[info.date] = {
      array: [],
      pastDate: pastDate
    };

    if (pastDate) {
      updatePastDateTasks(TasksService.getTasks(ownerUUID), cachedDates[info.date].array, pastDate);
    } else if (info.date === null) {
      updateNoDateTasks(TasksService.getTasks(ownerUUID), cachedDates[info.date].array);
    } else {
      if (info.date === DateService.getTodayYYYYMMDD()) {
        updateTodayTasks(TasksService.getTasks(ownerUUID), cachedDates[info.date].array, info.date);
      } else {
        updateFutureDateTasks(TasksService.getTasks(ownerUUID), cachedDates[info.date].array, info.date);
      }
    }
  }

  function removeDistantDates(ownerUUID, cachedDates, info) {
    if (info.date !== null) {
      for (var date in cachedDates) {
        if (cachedDates.hasOwnProperty(date) && date !== 'all' && date !== null) {
          var difference = DateService.numberOfDaysBetweenYYYYMMDDs(date, info.date);
          if (difference > 2) {
            // Clear distant dates array from cache.
            delete cachedDates[date];
          }
        }
      }
      if (cachedDates[null]) {
        // Clear 'no date' tasks from cache.
        delete cachedDates[null];
      }
    }
  }

  $scope.getTasksArray = function(arrayType, info) {
    var ownerUUID = UISessionService.getActiveUUID();
    if (!cachedTasksArrays[ownerUUID]) cachedTasksArrays[ownerUUID] = {};

    if (arrayType === 'all') {
      if (!cachedTasksArrays[ownerUUID]['all']) updateAllTasks(cachedTasksArrays[ownerUUID], ownerUUID);
      return cachedTasksArrays[ownerUUID]['all'];
    } else if (arrayType === 'date') {
      if (!cachedTasksArrays[ownerUUID]['date'])
        cachedTasksArrays[ownerUUID]['date'] = {};

      if (!cachedTasksArrays[ownerUUID]['date'][info.date]) {
        updateDateTasks(ownerUUID, cachedTasksArrays[ownerUUID]['date'], info);
        removeDistantDates(ownerUUID, cachedTasksArrays[ownerUUID]['date'], info);
      }

      return cachedTasksArrays[ownerUUID]['date'][info.date].array;
    }
  };

  $scope.getNewTask = function(initialValues){
    return TasksService.getNewTask(initialValues, UISessionService.getActiveUUID());
  };

  $scope.repeatTypes = [
  {
    trans: {title: 'daily'}
  },
  {
    trans: {title: 'weekly'}
  },
  {
    trans: {title: 'monthly'}
  },
  {
    trans: {title:'yearly'}
  }
  ];

  $scope.openTaskEditor = function openTaskEditor(task) {
    return $scope.openEditor('task', task);
  };

  /*
  * Every task in this list will not change in lists using ng-repeat.
  * Relates to isTaskVisible filter filter function,
  * and getTaskModifiedOrder and getTaskOrder orderBy filter functions, for example:
  *
  *

  *
  */
  var freezedTasksInLists = [];

  $scope.isTaskFrozen = function(task) {
    return freezedTasksInLists.findFirstIndexByKeyValue('task', task) !== undefined;
  };

  $scope.getTaskModified = function(task){
    var frozenTask = freezedTasksInLists.findFirstObjectByKeyValue('task', task);
    if (frozenTask !== undefined){
      return frozenTask.modified; // This is task.trans.modified
    }else {
      return task.trans.modified;
    }
  };

  // lock task in lists
  function freezeTask(task) {

    var taskIndex = freezedTasksInLists.findFirstIndexByKeyValue('task', task);

    if (taskIndex === undefined) {
      freezedTasksInLists.push({
        task: task,
        modified: task.trans.modified
      });
    } else {
      // Freeze some more.
      freezedTasksInLists[taskIndex].needsForce = true;
    }
  }

  // Release locked task in lists.
  function unfreezeTask(task, force) {

    var taskIndex = freezedTasksInLists.findFirstIndexByKeyValue('task', task);

    // Remove found task if we have the force or task does not need force to be unfrozen.
    if (taskIndex !== undefined && (force || !freezedTasksInLists[taskIndex].needsForce)) {
      freezedTasksInLists.splice(taskIndex, 1);
    }
  }

  /*
  * Toggle task complete.
  *
  * Lock task in lists before its deferred complete is settled.
  *
  * Return checkbox checked statuses.
  */
  $scope.toggleCompleteTask = function(task, taskCompletingReadyDeferred) {

    // Don't try to complete a task that hasn't been saved, saveTask will call this again
    // after the task has a uuid
    if (!task.trans.uuid || task.trans.itemType !== 'task') {
      // optimisticComplete already reflects the end value.
      return task.trans.optimisticComplete();
    }

    if (taskCompletingReadyDeferred) {
      taskCompletingReadyDeferred.promise.then(function(task) {
        unfreezeTask(task);
        // Need to delay combining task arrays to prevent immediate disapper
        $scope.combineTasksArrays();
      });
    }

    // Vibrate
    if (navigator.vibrate && !$scope.isVibrationDisabled())
      navigator.vibrate(200);

    freezeTask(task);
    if (task.trans.completed) {
      AnalyticsService.do('uncompleteTask');
      TasksService.uncompleteTask(task, UISessionService.getActiveUUID()).then(function(task) {
        unfreezeTask(task, true);
        $scope.combineTasksArrays();
      }, function() {
        unfreezeTask(task, true);
      });
      return false;
    } else {
      AnalyticsService.do('completeTask');
      TasksService.completeTask(task, UISessionService.getActiveUUID()).then(function(){
        if (!taskCompletingReadyDeferred){
          unfreezeTask(task, true);
          $scope.combineTasksArrays();
        }
      }, function() {
        if (!taskCompletingReadyDeferred){
          unfreezeTask(task, true);
          $scope.combineTasksArrays();
        }
      });
      return true;
    }
  };

  // SAVING

  $scope.saveTask = function(task) {
    var completeOnSave;
    if (task.trans.uuid){
      AnalyticsService.do('saveTask');
    }else{
      AnalyticsService.do('addTask');
      completeOnSave = task.trans.optimisticComplete();
    }

    return TasksService.saveTask(task, UISessionService.getActiveUUID()).then(function(result) {
      if (result === 'new' && completeOnSave){
        $scope.toggleCompleteTask(task);
      }
      return task;
    });
  };

  // (UN)DELETING

  $scope.deleteTask = function(task) {
    if (task.trans.uuid){
      AnalyticsService.do('deleteTask');
      return TasksService.deleteTask(task, UISessionService.getActiveUUID());
    }
  };

  $scope.undeleteTask = function(task) {
    if (task.trans.uuid){
      AnalyticsService.do('undeleteTask');
      TasksService.undeleteTask(task, UISessionService.getActiveUUID());
    }
  };

  // UI

  $scope.getNewTaskWithContext = function(context){
    if (context) return $scope.getNewTask({context: context});
    else return $scope.getNewTask();
  };

  // NAVIGATION

  $scope.context = undefined;

  function refreshFeatureMapHeading(){
    $scope.$evalAsync(function(){
      if ($scope.context)
        $scope.getFeatureMap('tasks').slides.right.heading = '@' + $scope.context.trans.title;
      else
        $scope.getFeatureMap('tasks').slides.right.heading = 'no context';
    });
  }

  $scope.swipeToContext = function(context){
    $scope.context = context;
    SwiperService.swipeTo('tasks/context');
    refreshFeatureMapHeading();
  };

  $scope.swipeToContextsAndReset = function(){
    $scope.context = undefined;
    SwiperService.swipeTo('tasks/contexts');
    refreshFeatureMapHeading();
  };

  $scope.getContextId = function() {
    return $scope.context ? $scope.context.trans.uuid : 'no';
  };

  // ONBOARDING

  function gotoSignUp(){
    $scope.changeFeature('user', undefined, true);
  }

  if (!$scope.onboardingInProgress && $scope.isOnboarded('focusTasks') && $scope.isFakeUser()){
    // Show modal immediately on cold boot
    UISessionService.pushDelayedNotification({
      type: 'signUp',
      gotoFn: gotoSignUp
    });
    $timeout(function() {
      UISessionService.activateDelayedNotifications();
    }, 1000);
  }
}

TasksController['$inject'] = [
'$rootScope', '$scope', '$timeout',
'AnalyticsService', 'ArrayService', 'DateService', 'ItemsService', 'SwiperService', 'TasksService',
'UISessionService'
];
angular.module('em.tasks').controller('TasksController', TasksController);
<commit_msg>Better handling of cached tasks with no date.<commit_after>/* Copyright 2013-2014 Extended Mind Technologies Oy
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
 'use strict';

 function TasksController($rootScope, $scope, $timeout,
                          AnalyticsService, ArrayService, DateService, ItemsService, SwiperService,
                          TasksService, UISessionService) {

  // INITIALIZING
  if (angular.isFunction($scope.registerArrayChangeCallback))
    $scope.registerArrayChangeCallback('tasks', 'active', invalidateTasksArraysWithActiveTasks,
                                       'TasksController');

  var cachedTasksArrays = {};

  /*
  * Invalidate cached active tasks arrays.
  *
  * @param {Object} task Changed task.
  */
  function invalidateTasksArraysWithActiveTasks(tasks, modifiedTask, ownerUUID) {
    if (cachedTasksArrays[ownerUUID]) {
      for (var arrayType in cachedTasksArrays[ownerUUID]) {
        if (cachedTasksArrays[ownerUUID].hasOwnProperty(arrayType)) {
          // Every cached tasks array has cached tasks.
          if (arrayType === 'all') {
            if ($scope.getActiveFeature() === 'tasks')
              updateAllTasks(cachedTasksArrays[ownerUUID], ownerUUID);
            else
              cachedTasksArrays[ownerUUID]['all'] = undefined;
          }
          else if (arrayType === 'date') {
            var focusActive = $scope.getActiveFeature() === 'focus';

            for (var date in cachedTasksArrays[ownerUUID]['date']) {
              if (cachedTasksArrays[ownerUUID]['date'].hasOwnProperty(date)) {
                if (focusActive)
                  updateDateTasks(ownerUUID, cachedTasksArrays[ownerUUID]['date'], {date: date});
                else
                  delete cachedTasksArrays[ownerUUID]['date'][date];
              }
            }
          }
        }
      }
    }
  }

  function updateAllTasks(cachedTasks, ownerUUID) {

    function compareWithFrozenModified(a, b) {
      return $scope.getTaskModified(a) - $scope.getTaskModified(b);
    }

    // Get tasks.
    var allActiveTasks = TasksService.getTasks(ownerUUID);
    cachedTasks['all'] = [];
    // Sort and cache array.
    for (var i = 0; i < allActiveTasks.length; i++) {
      ArrayService.insertItemToArray(allActiveTasks[i],
                                     cachedTasks['all'],
                                     compareWithFrozenModified,
                                     true);
    }
  }

  /*
  * Show completed on a past date.
  */
  function updatePastDateTasks(allActiveTasks, pastDateTasksArray, pastDateYYYYMMDD) {
    var pastDateMidnight = pastDateYYYYMMDD.yyyymmddToNoonDate().setHours(0, 0, 0, 0);

    for (var i = allActiveTasks.length - 1; i >= 0; i--) {
      var task = allActiveTasks[i];
      if (task.trans.completed && new Date(task.trans.completed).setHours(0, 0, 0, 0) === pastDateMidnight)
        pastDateTasksArray.push(task);
    }
  }

  /*
  * Show tasks without a date.
  */
  function updateNoDateTasks(allActiveTasks, noDateTasksArray) {
    for (var i = 0; i < allActiveTasks.length; i++) {
      if (!allActiveTasks[i].trans.due) noDateTasksArray.push(allActiveTasks[i]);
    }
  }

  /*
  * Show overdue and due tasks and completed on date.
  */
  function updateTodayTasks(allActiveTasks, todayTasks, todayYYYYMMDD) {
    var todayMidnight = DateService.getTodayDateWithoutTime();

    for (var i = allActiveTasks.length - 1; i >= 0; i--) {
      var task = allActiveTasks[i];
      if (task.trans.due && task.trans.due <= todayYYYYMMDD &&
          !(task.trans.completed && task.trans.completed < todayMidnight))
      {
        todayTasks.push(task);
      }
      else if (task.trans.completed && task.trans.completed > todayMidnight) {
        todayTasks.push(task);
      }
    }
  }

  /*
  * Show due tasks and completed on date.
  */
  function updateFutureDateTasks(allActiveTasks, futureDateTasks, futureYYYYMMDD) {
    var futureDateMidnight = futureYYYYMMDD.yyyymmddToNoonDate().setHours(0, 0, 0, 0);

    for (var i = allActiveTasks.length - 1; i >= 0; i--) {
      var task = allActiveTasks[i];
      if (task.trans.due && task.trans.due === futureYYYYMMDD) {
        futureDateTasks.push(task);
      } else if (task.trans.completed &&
                 new Date(task.trans.completed).setHours(0, 0, 0, 0) === futureDateMidnight)
      {
        futureDateTasks.push(task);
      }
    }
  }

  function updateDateTasks(ownerUUID, cachedDates, info) {
    var pastDate;

    if (cachedDates[info.date])
      pastDate = cachedDates[info.date].pastDate;
    else if (info.pastDate)
      pastDate = info.pastDate;

    cachedDates[info.date] = {
      array: [],
      pastDate: pastDate
    };

    if (pastDate) {
      updatePastDateTasks(TasksService.getTasks(ownerUUID), cachedDates[info.date].array, pastDate);
    } else if (info.date === 'noDate') {
      updateNoDateTasks(TasksService.getTasks(ownerUUID), cachedDates[info.date].array);
    } else {
      if (info.date === DateService.getTodayYYYYMMDD()) {
        updateTodayTasks(TasksService.getTasks(ownerUUID), cachedDates[info.date].array, info.date);
      } else {
        updateFutureDateTasks(TasksService.getTasks(ownerUUID), cachedDates[info.date].array, info.date);
      }
    }
  }

  function removeDistantDates(cachedDates, info) {
    if (info.date !== 'noDate') {

      if (cachedDates['noDate']) {
        // Clear 'no date' tasks from cache.
        delete cachedDates['noDate'];
      }

      for (var date in cachedDates) {
        if (cachedDates.hasOwnProperty(date)) {
          var difference = DateService.numberOfDaysBetweenYYYYMMDDs(date, info.date);
          if (difference > 2) {
            // Clear distant dates array from cache.
            delete cachedDates[date];
          }
        }
      }
    }
  }

  $scope.getTasksArray = function(arrayType, info) {
    var ownerUUID = UISessionService.getActiveUUID();
    if (!cachedTasksArrays[ownerUUID]) cachedTasksArrays[ownerUUID] = {};

    if (arrayType === 'all') {
      if (!cachedTasksArrays[ownerUUID]['all']) updateAllTasks(cachedTasksArrays[ownerUUID], ownerUUID);
      return cachedTasksArrays[ownerUUID]['all'];
    } else if (arrayType === 'date') {
      if (!cachedTasksArrays[ownerUUID]['date'])
        cachedTasksArrays[ownerUUID]['date'] = {};

      if (info.date === null) {
        // Use 'noDate' to identify cached tasks without date instead of null.
        info.date = 'noDate';
      }

      if (!cachedTasksArrays[ownerUUID]['date'][info.date]) {
        updateDateTasks(ownerUUID, cachedTasksArrays[ownerUUID]['date'], info);
        removeDistantDates(cachedTasksArrays[ownerUUID]['date'], info);
      }

      return cachedTasksArrays[ownerUUID]['date'][info.date].array;
    }
  };

  $scope.getNewTask = function(initialValues){
    return TasksService.getNewTask(initialValues, UISessionService.getActiveUUID());
  };

  $scope.repeatTypes = [
  {
    trans: {title: 'daily'}
  },
  {
    trans: {title: 'weekly'}
  },
  {
    trans: {title: 'monthly'}
  },
  {
    trans: {title:'yearly'}
  }
  ];

  $scope.openTaskEditor = function openTaskEditor(task) {
    return $scope.openEditor('task', task);
  };

  /*
  * Every task in this list will not change in lists using ng-repeat.
  * Relates to isTaskVisible filter filter function,
  * and getTaskModifiedOrder and getTaskOrder orderBy filter functions, for example:
  *
  *

  *
  */
  var freezedTasksInLists = [];

  $scope.isTaskFrozen = function(task) {
    return freezedTasksInLists.findFirstIndexByKeyValue('task', task) !== undefined;
  };

  $scope.getTaskModified = function(task){
    var frozenTask = freezedTasksInLists.findFirstObjectByKeyValue('task', task);
    if (frozenTask !== undefined){
      return frozenTask.modified; // This is task.trans.modified
    }else {
      return task.trans.modified;
    }
  };

  // lock task in lists
  function freezeTask(task) {

    var taskIndex = freezedTasksInLists.findFirstIndexByKeyValue('task', task);

    if (taskIndex === undefined) {
      freezedTasksInLists.push({
        task: task,
        modified: task.trans.modified
      });
    } else {
      // Freeze some more.
      freezedTasksInLists[taskIndex].needsForce = true;
    }
  }

  // Release locked task in lists.
  function unfreezeTask(task, force) {

    var taskIndex = freezedTasksInLists.findFirstIndexByKeyValue('task', task);

    // Remove found task if we have the force or task does not need force to be unfrozen.
    if (taskIndex !== undefined && (force || !freezedTasksInLists[taskIndex].needsForce)) {
      freezedTasksInLists.splice(taskIndex, 1);
    }
  }

  /*
  * Toggle task complete.
  *
  * Lock task in lists before its deferred complete is settled.
  *
  * Return checkbox checked statuses.
  */
  $scope.toggleCompleteTask = function(task, taskCompletingReadyDeferred) {

    // Don't try to complete a task that hasn't been saved, saveTask will call this again
    // after the task has a uuid
    if (!task.trans.uuid || task.trans.itemType !== 'task') {
      // optimisticComplete already reflects the end value.
      return task.trans.optimisticComplete();
    }

    if (taskCompletingReadyDeferred) {
      taskCompletingReadyDeferred.promise.then(function(task) {
        unfreezeTask(task);
        // Need to delay combining task arrays to prevent immediate disapper
        $scope.combineTasksArrays();
      });
    }

    // Vibrate
    if (navigator.vibrate && !$scope.isVibrationDisabled())
      navigator.vibrate(200);

    freezeTask(task);
    if (task.trans.completed) {
      AnalyticsService.do('uncompleteTask');
      TasksService.uncompleteTask(task, UISessionService.getActiveUUID()).then(function(task) {
        unfreezeTask(task, true);
        $scope.combineTasksArrays();
      }, function() {
        unfreezeTask(task, true);
      });
      return false;
    } else {
      AnalyticsService.do('completeTask');
      TasksService.completeTask(task, UISessionService.getActiveUUID()).then(function(){
        if (!taskCompletingReadyDeferred){
          unfreezeTask(task, true);
          $scope.combineTasksArrays();
        }
      }, function() {
        if (!taskCompletingReadyDeferred){
          unfreezeTask(task, true);
          $scope.combineTasksArrays();
        }
      });
      return true;
    }
  };

  // SAVING

  $scope.saveTask = function(task) {
    var completeOnSave;
    if (task.trans.uuid){
      AnalyticsService.do('saveTask');
    }else{
      AnalyticsService.do('addTask');
      completeOnSave = task.trans.optimisticComplete();
    }

    return TasksService.saveTask(task, UISessionService.getActiveUUID()).then(function(result) {
      if (result === 'new' && completeOnSave){
        $scope.toggleCompleteTask(task);
      }
      return task;
    });
  };

  // (UN)DELETING

  $scope.deleteTask = function(task) {
    if (task.trans.uuid){
      AnalyticsService.do('deleteTask');
      return TasksService.deleteTask(task, UISessionService.getActiveUUID());
    }
  };

  $scope.undeleteTask = function(task) {
    if (task.trans.uuid){
      AnalyticsService.do('undeleteTask');
      TasksService.undeleteTask(task, UISessionService.getActiveUUID());
    }
  };

  // UI

  $scope.getNewTaskWithContext = function(context){
    if (context) return $scope.getNewTask({context: context});
    else return $scope.getNewTask();
  };

  // NAVIGATION

  $scope.context = undefined;

  function refreshFeatureMapHeading(){
    $scope.$evalAsync(function(){
      if ($scope.context)
        $scope.getFeatureMap('tasks').slides.right.heading = '@' + $scope.context.trans.title;
      else
        $scope.getFeatureMap('tasks').slides.right.heading = 'no context';
    });
  }

  $scope.swipeToContext = function(context){
    $scope.context = context;
    SwiperService.swipeTo('tasks/context');
    refreshFeatureMapHeading();
  };

  $scope.swipeToContextsAndReset = function(){
    $scope.context = undefined;
    SwiperService.swipeTo('tasks/contexts');
    refreshFeatureMapHeading();
  };

  $scope.getContextId = function() {
    return $scope.context ? $scope.context.trans.uuid : 'no';
  };

  // ONBOARDING

  function gotoSignUp(){
    $scope.changeFeature('user', undefined, true);
  }

  if (!$scope.onboardingInProgress && $scope.isOnboarded('focusTasks') && $scope.isFakeUser()){
    // Show modal immediately on cold boot
    UISessionService.pushDelayedNotification({
      type: 'signUp',
      gotoFn: gotoSignUp
    });
    $timeout(function() {
      UISessionService.activateDelayedNotifications();
    }, 1000);
  }
}

TasksController['$inject'] = [
'$rootScope', '$scope', '$timeout',
'AnalyticsService', 'ArrayService', 'DateService', 'ItemsService', 'SwiperService', 'TasksService',
'UISessionService'
];
angular.module('em.tasks').controller('TasksController', TasksController);
<|endoftext|>


<commit_before>var defaults = {
  bracket: null,
  bounds: null,
  method: 'Brent',
  tol: null,
  options: null
};

var extendNoOverwrite = function (obj, defaults){
  for (key in defaults) {
    if (obj[key] === undefined) {
      obj[key] = defaults[key];
    }
  }
}

var extractFunc = function (func) {
  func = func.toString();
  func = func.match(/return[\s\S]*}/)[0];
  func = func.replace(/return/, '');
  func = func.replace(/[{};\n\r]/g, '');
  return func;
}

var cleanFunc = function (func, newVarName){
  if (typeof func === 'function') {
    func = extractFunc(func);
  }

  if (newVarName && typeof newVarName === 'string') {
    var regex = new RegExp('\\b' + newVarName + '\\b', 'g');
    func = func.replace(regex, 'x');
  }

  func = func.toLowerCase();
  func = func.replace(/Math./g,'');
  func = func.replace(/LN2/g, 'ln(2)');
  func = func.replace(/LN10/g, 'ln(10)');
  func = func.replace(/LOG2E/g, 'log(e,2)');
  func = func.replace(/LOG10E/g, 'log(e,10)');
  func = func.replace(/SQRT1_2/g, 'sqrt(1/2)');
  func = func.replace(/SQRT2/g, 'sqrt(2)');

  return func;
}

module.exports = {
  cleanMin: function (func, options, callback) {
    if (typeof options === 'function') {
      callback = options;
      options = undefined;
    }

    this.cleanCB(callback);

    options = options || {};

    func = cleanFunc(func, options.variable);
    // console.log(func);

    options = {
      bracket: null,
      method: 'Brent',
      bounds: options.bounds,
      tol: null,
      options: null
    }

    // extendNoOverwrite(options, defaults);

    if (Array.isArray(options.bounds) && options.bounds.length === 2) {
      options.method = 'Bounded';
    } else {
      options.bounds = null;
      options.method = 'Brent';
    }

    return {
      func:     func,
      options:  options,
      callback: callback
    }
  },

  cleanCB: function(callback) {
    return callback || function (results){
      console.log(results);
    };
  }
}<commit_msg>More intelligently clean function with regex<commit_after>var globalDefaults = {
  guess:               0,
  iterations:          100,
  temperature:         1.0,
  stepSize:            0.5,
  includeAllMinsFound: false,
  interval:            50
};

var extendNoOverwrite = function (obj, defaults){
  for (key in defaults) {
    if (obj[key] === undefined) {
      obj[key] = defaults[key];
    }
  }
}

var extractFunc = function (func) {
  // if function given is a function,
  // extract the argument name, extract the return value,
  // replace the argument name with 'x'
  // return the return value as a string, with x as the independent variable
  func = func.toString();
  var arg = func.match(/^function\s*[^\(]*\(\s*([^\)\,]*)[\,\)]/m)[1];
  var regex = new RegExp('\\b' + arg + '\\b', 'g')
  func = func.replace(regex, 'x');
  func = func.match(/return\s*([^\;}]*)[\;}]/m)[1];
  // func = func.replace(/Math./g, '');
  return func;
}

var cleanFunc = function (func, newVarName){
  if (typeof func === 'function') {
    func = extractFunc(func);
  } else if (typeof func === 'string') {
    if (newVarName && typeof newVarName === 'string') {
      var regex = new RegExp('\\b' + newVarName + '\\b', 'g');
      func = func.replace(regex, 'x');
    }
  } else {
    throw 'function to be optimized needs to be a function or a string expression';
  }

  func = func.replace(/Math./gi,'');
  func = func.replace(/LN2/gi, 'ln(2)');
  func = func.replace(/LN10/gi, 'ln(10)');
  func = func.replace(/LOG2E/gi, 'log(e,2)');
  func = func.replace(/LOG10E/gi, 'log(e,10)');
  func = func.replace(/SQRT1_2/gi, 'sqrt(1/2)');
  func = func.replace(/SQRT2/gi, 'sqrt(2)');
  func = func.toLowerCase();

  return func;
}

module.exports = {
  cleanMin: function (operation, func, options, callback) {
    if (typeof options === 'function') {
      callback = options;
      options = undefined;
    }
    // provide default callback function (console.log)
    callback = this.cleanCB(callback);

    options = options || {};

    // clean provided function to be accepted by sympy lambdify function
    func = cleanFunc(func, options.variable);

    // provide sensible defaults for the options object
    if (operation === 'local') {
      options = {
        bracket: null,
        method: 'Brent',
        bounds: options.bounds,
        tol: null,
        options: null
      }

      if (Array.isArray(options.bounds) && options.bounds.length === 2) {
        options.method = 'Bounded';
      } else {
        options.bounds = null;
        options.method = 'Brent';
      }
    } else if (operation === 'global') {
      extendNoOverwrite(options, globalDefaults);
    }

    return {
      func:     func,
      options:  options,
      callback: callback
    }
  },

  cleanCB: function(callback) {
    return callback || function (results){
      console.log(results);
    };
  }
}<|endoftext|>


<commit_before>#!/usr/bin/env node

require('./proof')(3, function (step, Strata, tmp, deepEqual, serialize, gather, load, objectify, say) {
    var strata = new Strata({ directory: tmp, leafSize: 3, branchSize: 3 }), fs = require('fs')
    step(function () {
        serialize(__dirname + '/fixtures/merge.before.json', tmp, step())
    }, function () {
        strata.open(step())
    }, function () {
        strata.mutator('a', step())
    }, function (cursor) {
        step(function () {
            cursor.indexOf('a', step())
        }, function (index) {
            cursor.remove(index, step())
        }, function () {
            cursor.indexOf('b', step())
        }, function (index) {
            cursor.remove(index, step())
        }, function () {
            cursor.unlock()
        })
    }, function () {
        gather(step, strata)
    }, function (records) {
        deepEqual(records, [ 'c', 'd' ], 'records')
        strata.balance(step())
    }, function () {
        objectify(tmp, step())
        load(__dirname + '/fixtures/left-empty.after.json', step())
    }, function (actual, expected) {
        say(expected)
        say(actual)

        deepEqual(actual, expected, 'merge')
    }, function () {
        gather(step, strata)
    }, function (records) {
        deepEqual(records, [ 'c', 'd' ], 'merged')
    }, function() {
        strata.close(step())
    })
})
<commit_msg>Tidy `t/merge/left-empty.t.js`.<commit_after>#!/usr/bin/env node

require('./proof')(3, function (step, Strata, tmp, load, serialize, objectify, gather, say, deepEqual) {
    var strata = new Strata({ directory: tmp, leafSize: 3, branchSize: 3 })
    step(function () {
        serialize(__dirname + '/fixtures/merge.before.json', tmp, step())
    }, function () {
        strata.open(step())
    }, function () {
        strata.mutator('a', step())
    }, function (cursor) {
        step(function () {
            cursor.indexOf('a', step())
        }, function (index) {
            cursor.remove(index, step())
        }, function () {
            cursor.indexOf('b', step())
        }, function (index) {
            cursor.remove(index, step())
        }, function () {
            cursor.unlock()
        })
    }, function () {
        gather(step, strata)
    }, function (records) {
        deepEqual(records, [ 'c', 'd' ], 'records')
        strata.balance(step())
    }, function () {
        objectify(tmp, step())
        load(__dirname + '/fixtures/left-empty.after.json', step())
    }, function (actual, expected) {
        say(expected)
        say(actual)

        deepEqual(actual, expected, 'merge')
    }, function () {
        gather(step, strata)
    }, function (records) {
        deepEqual(records, [ 'c', 'd' ], 'merged')
    }, function() {
        strata.close(step())
    })
})
<|endoftext|>


<commit_before>var builder = require('focus').component.builder;
var React = require('react');
var type = require('focus').component.types;
var popinProperties = require('../mixin/popin-behaviour').mixin;

var menuMixin = {
  mixins: [popinProperties],

  /** @inheritedProps*/
  getDefaultProps: function getMenuDefaultProps() {
    return {
      links: []
    };
  },
  /**
   * Toggle the state of the menu.
   */
  toggle: function toggleOpenMenu() {
    this.setState({open: !this.state.open});
  },
  /**
   * Render the links of the menu
   */
  renderLinks: function () {
    return this.props.links.map((link)=> {
      return (
        {link.name}
      );
    });
  },
  /**
   * Render the title content
   * @returns {XML} - title content
   */
  renderTitle: function () {
    return
{this.props.title}
;
  },
  /** @inheriteddoc */
  render: function () {
    var className = `menu menu-${this.props.direction} menu-${this.props.position} menu-${this.state.open ? 'open' : ''}`;
    if (this.props.style.className !== undefined && this.props.style.className !== null) {
      className = className + ' ' + this.props.style.className;
    }
    return (

      {this.renderTitle()}
      {this.renderContent()}

    );
  }
};

module.exports = builder(menuMixin);
<commit_msg>Remove method render title from menu component<commit_after>var builder = require('focus').component.builder;
var React = require('react');
var type = require('focus').component.types;
var popinProperties = require('../mixin/popin-behaviour').mixin;

var menuMixin = {
  mixins: [popinProperties],

  /** @inheritedProps*/
  getDefaultProps: function getMenuDefaultProps() {
    return {
      links: []
    };
  },
  /**
   * Toggle the state of the menu.
   */
  toggle: function toggleOpenMenu() {
    this.setState({open: !this.state.open});
  },
  /**
   * Render the links of the menu
   */
  renderLinks: function () {
    return this.props.links.map((link)=> {
      return (
        {link.name}
      );
    });
  },
  /** @inheriteddoc */
  render: function () {
    var className = `menu menu-${this.props.direction} menu-${this.props.position} menu-${this.state.open ? 'open' : ''}`;
    if (this.props.style.className !== undefined && this.props.style.className !== null) {
      className = className + ' ' + this.props.style.className;
    }
    return (

      {this.renderTitle()}
      {this.renderContent()}

    );
  }
};

module.exports = builder(menuMixin);
<|endoftext|>


<commit_before>import { MiddlewareRegistry } from '../base/redux';
import { LIB_DID_INIT } from '../base/lib-jitsi-meet';

import { initAnalytics } from './functions';

/**
 * Middleware which intercepts config actions to handle evaluating analytics
 * config based on the config stored in the store.
 *
 * @param {Store} store - The redux store.
 * @returns {Function}
 */
MiddlewareRegistry.register(store => next => action => {
    switch (action.type) {
    case LIB_DID_INIT: {
        initAnalytics(store);
        break;
    }
    }

    return next(action);
});
<commit_msg>fix(analytics): Room name persistant prop.<commit_after>import { SET_ROOM } from '../base/conference';
import { MiddlewareRegistry } from '../base/redux';

import { initAnalytics } from './functions';

/**
 * Middleware which intercepts config actions to handle evaluating analytics
 * config based on the config stored in the store.
 *
 * @param {Store} store - The redux store.
 * @returns {Function}
 */
MiddlewareRegistry.register(store => next => action => {
    switch (action.type) {
    case SET_ROOM: {
        const result = next(action);

        initAnalytics(store);

        return result;
    }
    }

    return next(action);
});
<|endoftext|>


<commit_before>jQuery.noConflict(); // Allow the test to run with other libs or jQuery's.

// load testswarm agent
(function() {
	var url = window.location.search;
	url = decodeURIComponent( url.slice( url.indexOf("swarmURL=") + 9 ) );
	if ( !url || url.indexOf("http") !== 0 ) {
		return;
	}

	// (Temporarily) Disable Ajax tests to reduce network strain
	QUnit.isLocal = true;

	document.write("<scr" + "ipt src='http://swarm.jquery.org/js/inject.js?" + (new Date).getTime() + "'></scr" + "ipt>");
})();
<commit_msg>Need to expose the isLocal change globally as well.<commit_after>jQuery.noConflict(); // Allow the test to run with other libs or jQuery's.

// load testswarm agent
(function() {
	var url = window.location.search;
	url = decodeURIComponent( url.slice( url.indexOf("swarmURL=") + 9 ) );
	if ( !url || url.indexOf("http") !== 0 ) {
		return;
	}

	// (Temporarily) Disable Ajax tests to reduce network strain
	isLocal = QUnit.isLocal = true;

	document.write("<scr" + "ipt src='http://swarm.jquery.org/js/inject.js?" + (new Date).getTime() + "'></scr" + "ipt>");
})();
<|endoftext|>


<commit_before>window.c.contribution.projectFilters = ((m, h, moment) => {
    return () =>{
        const filters = m.postgrest.filtersVM,

              nearMe = filters({
                  near_me: 'eq',
                  open_for_contributions: 'eq'
              }).near_me(true),

              expiring = filters({
                  expires_at: 'lte',
                  open_for_contributions: 'eq'
              }).open_for_contributions('true').expires_at(moment().add(14, 'days').format('YYYY-MM-DD')),

              recent = filters({
                  online_date: 'gte',
                  open_for_contributions: 'eq'
              }).open_for_contributions('true').online_date(moment().subtract(5, 'days').format('YYYY-MM-DD')),

              recommended = filters({
                  recommended: 'eq',
                  open_for_contributions: 'eq'
              }).recommended('true').open_for_contributions('true'),

              online = filters({
                  open_for_contributions: 'eq'
              }).open_for_contributions('true'),

              successful = filters({
                  state: 'eq'
              }).state('successful');

        return {
            recommended: {
                title: 'Recomendados',
                filter: recommended
            },
            online: {
                title: 'No ar',
                filter: online
            },
            expiring: {
                title: 'Reta final',
                filter: expiring
            },
            successful: {
                title: 'Bem-sucedidos',
                filter: successful
            },
            recent: {
                title: 'Recentes',
                filter: recent
            },
            near_me: {
                title: 'Próximos a mim',
                filter: nearMe
            }
        };
    };
}(window.m, window.c.h, window.moment));
<commit_msg>Optimizes some explore filters to avoid statement timeout<commit_after>window.c.contribution.projectFilters = ((m, h, moment) => {
    return () =>{
        const filters = m.postgrest.filtersVM,

              nearMe = filters({
                  near_me: 'eq',
                  open_for_contributions: 'eq',
                  online_date: 'gt'
              }).near_me(true).online_date(moment().subtract(30, 'days').format('YYYY-MM-DD')),

              expiring = filters({
                  expires_at: 'lte',
                  open_for_contributions: 'eq'
              }).open_for_contributions('true').expires_at(moment().add(14, 'days').format('YYYY-MM-DD')),

              recent = filters({
                  online_date: 'gte',
                  open_for_contributions: 'eq'
              }).open_for_contributions('true').online_date(moment().subtract(5, 'days').format('YYYY-MM-DD')),

              recommended = filters({
                  recommended: 'eq',
                  open_for_contributions: 'eq'
              }).recommended('true').open_for_contributions('true'),

              online = filters({
                  open_for_contributions: 'eq'
              }).open_for_contributions('true'),

              successful = filters({
                  state: 'eq',
                  expires_at: 'gt'
              }).state('successful').expires_at(moment().subtract(30, 'days').format('YYYY-MM-DD'));

        return {
            recommended: {
                title: 'Recomendados',
                filter: recommended
            },
            online: {
                title: 'No ar',
                filter: online
            },
            expiring: {
                title: 'Reta final',
                filter: expiring
            },
            successful: {
                title: 'Bem-sucedidos',
                filter: successful
            },
            recent: {
                title: 'Recentes',
                filter: recent
            },
            near_me: {
                title: 'Próximos a mim',
                filter: nearMe
            }
        };
    };
}(window.m, window.c.h, window.moment));
<|endoftext|>


<commit_before>'use strict';

/**
 * @ngdoc function
 * @name ratemycoopApp.controller:ReviewformCtrl
 * @description
 * # ReviewformCtrl
 * Controller of the ratemycoopApp
 */
angular.module('ratemycoopApp')
  .controller('ReviewformCtrl', function ($scope, Company, Major, City, Perk, PayType, User, $routeParams, $location) {
    $scope.loading = {
      main: true, // Promise, must be turned to false somewhere
      perks: true // Promise, must be turned to false somewhere
    };

    /**
     * Initialize Session
     */
    if (User.isAuthenticated()) {
      $scope.user = User.getCurrent(
        function (success) {
          $scope.formData.userId = success.id;
        }
      );
    } else {
      console.log('ERROR: User must be authenticated');
      $location.path('/login');
      $scope.user = null;
    }

    /**
     * Used if we want to fetch for pay types, as opposed to using just
     * $scope.payTypes
     */
    $scope.fetchedPayTypes = PayType.find({},
      function (success) {
      });

    $scope.payTypes = [{id: 1, name: "Hourly"}, {id: 2, name: "Salary"}, {id: 3, name: "Stipend"}];

    /**
     * Get company info on-load
     */
    $scope.company = Company.findOne({
        filter: {
          where: {name: $routeParams.companyname},
          include: ['perks', 'majors', 'reviews', {'locations': 'region'}]
        }
      },
      function (successData) {
        $scope.loading.main = false;
      }
    );

    /**
     * Get major info on-load
     */
    $scope.majors = Major.find({},
      function (successData) {
        angular.forEach(successData, function (result) {
          result.title = result.name;
          result.description = result.code;
        });

        $('#majorSearch').search({
          source: $scope.majors,
          maxResults: 4,
          searchFields: ['code', 'name']
        })
      }
    );

    $scope.allPerks = Perk.find({},
      function (successData) {
        angular.forEach(successData, function (value) {
          value['isSelected'] = false;
        });
        $scope.loading.perks = false;
        // Success allperks
      }
    );

    $scope.togglePerkAddition = function (perk) {
      if (!perk.isSelected) {
        perk.isSelected = true; //This toggles ui change
        $scope.formData.perks.push(perk.id); // This adds only id, to formData.perks
      } else {
        $scope.$watch(perk, function (test) {
          console.log('in test');
        });
        console.log('unselecting perk');
        perk.isSelected = false; // this toggles ui change
        var i = $scope.formData.perks.indexOf(perk.id); // this finds and removes perk id from perks
        if (i > -1) {
          $scope.formData.perks.splice(i, 1);
        }
      }
    };

    $scope.formData = {
      overallRating: 0,
      cultureRating: 0,
      difficultyRating: 0,

      description: "",

      pay: "",
      payTypeId: $scope.payTypes[0].id,

      //userId: $scope.user.id == set when async call to USER returns

      perks: [],

      returnOffer: false,
      recommend: false,
      anonymous: true

    };

    /**
     * Actual final SERVICE push to Backend.
     */
    $scope.submitReview = function () {
      $scope.loading.main = true;
      $scope.loading.perks = true;
      console.log("Review is being submitted :) ");
      var pushObject = prepForPush($scope.formData);
      console.log("final submission object is");
      console.log(pushObject);

      Company.reviews.create(
        {id: $scope.company.id},
        pushObject,
        function (success) {
          console.log('success!');

          $scope.loading.main = false;
          $scope.loading.perks = false;
          $location.path('/company/' + $scope.company.name);
        },
        function (error) {
          // TODO handle review creation error
          $scope.loading.main = false;
          $scope.loading.perks = false;
        }
      );

    };

    /**
     * Prepares the formData parameter object to correct keys in order to push to API
     * @param formData
     */
    function prepForPush(formData) {
      // Validate pay input - grabs the pure currency as a string, else null
      if (formData.pay !== "") {
        var isValidPay = formData.pay.search(/^\$?[\d,]+(\.\d*)?$/) >= 0;
        if (isValidPay) {
          formData.pay = formData.pay.replace(/[^0-9\.]/g, '');
        } else {
          formData.pay = null;
        }
      } else {
        formData.pay = null;
      }

      var perksList = [];
      angular.forEach(formData.perks, function (perk) {
        perksList.push(perk.id);
      });

      var majorsList = [];
      majorsList.push($('#majorSearch').search('get result').id);

      var temploc = $('#locationSearch').search('get result').id;

      var pushObj = {
        anonymous: formData.anonymous,
        returnOffer: formData.returnOffer,
        recommend: formData.recommend,
        description: formData.description,
        overallRating: formData.overallRating,
        cultureRating: formData.cultureRating,
        difficultyRating: formData.cultureRating,
        pay: formData.pay,
        userId: formData.userId,
        payTypeId: formData.payTypeId,

        perks: perksList,
        majors: majorsList,
        location: temploc
      };

      return pushObj;
    }

    /**
     * Wizard variables.
     * @type {{currStep: string, s1: string, s2: string, s3: string}}
     */
    $scope.wizard = {
      currStep: "wizardStepOne",
      s1: "wizardStepOne",
      s2: "wizardStepTwo",
      s3: "wizardStepThree"
    };

    // On document ready, wait half a second and to init Semantic UI elements.
    angular.element(document).ready(function () {
      // Delay to wait for angular to
      setTimeout(setupSemantic, 0);
    });

    /**
     * Runs once on document ready. Set up Semantic UI elements
     */
    function setupSemantic() {
      $('.ui.accordion').accordion();
      $('.ui.selection.dropdown').dropdown();
      $('#payType').dropdown();
      $('.rating').rating({
        /* see http://bit.ly/1M0OaL9 of why we need to do this */
        onRate: function (val) {
          $scope.$apply(function () {
            var ratings = $('.rating').rating('get rating');
            $scope.formData.overallRating = ratings[0];
            $scope.formData.cultureRating = ratings[1];
            $scope.formData.difficultyRating = ratings[2];
          });
        }
      });
      $('#privacyCheckbox').checkbox({
        onChange: function () {
          /* see http://bit.ly/1M0OaL9 of why we need to do this */
          $scope.$apply(function () {
            $scope.formData.anonymous = !$scope.formData.anonymous;
          });
        }
      });
      $('#returnOfferCheckbox').checkbox({
        onChange: function () {
          /* see http://bit.ly/1M0OaL9 of why we need to do this */
          $scope.$apply(function () {
            $scope.formData.returnOffer = !$scope.formData.returnOffer;
          });
        }
      });
      $('#recommendCheckbox').checkbox({
        onChange: function () {
          /* see http://bit.ly/1M0OaL9 of why we need to do this */
          $scope.$apply(function () {
            $scope.formData.recommend = !$scope.formData.recommend;
          });
        }
      });

      $('#locationSearch').search({
        apiSettings: {
          //TODO: not hardcode url, use another system for searching
          url: "https://ratemycoop.io/api/v1/Cities/search?query={query}"
        },
        searchFullText: false,
        searchFields: ['title'],
        maxResults: 10
      });

      $('#reviewForm').form(
        {
          major: {
            identifier: 'majorSearchInput',
            rules: [{
              type: 'empty',
              prompt: 'Please enter your major'
            }]
          },
          city: {
            identifier: 'locationSearchInput',
            rules: [{
              type: 'empty',
              prompt: 'Please enter your major'
            }]
          },
          pay: {
            identifier: 'payInput',
            rules: [
              {
                type: 'empty',
                prompt: 'Please enter a valid pay'
              },
              {
                type: 'length[2]'
              }
            ]
          },
          payType: {
            identifier: 'payTypeInput',
            prompt: 'prompt'
          }
        },
        {
          on: 'blur'
        }
      );
    }

  });
<commit_msg>- fixed sending null<commit_after>'use strict';

/**
 * @ngdoc function
 * @name ratemycoopApp.controller:ReviewformCtrl
 * @description
 * # ReviewformCtrl
 * Controller of the ratemycoopApp
 */
angular.module('ratemycoopApp')
  .controller('ReviewformCtrl', function ($scope, Company, Major, City, Perk, PayType, User, $routeParams, $location) {
    $scope.loading = {
      main: true, // Promise, must be turned to false somewhere
      perks: true // Promise, must be turned to false somewhere
    };

    /**
     * Initialize Session
     */
    if (User.isAuthenticated()) {
      $scope.user = User.getCurrent(
        function (success) {
          $scope.formData.userId = success.id;
        }
      );
    } else {
      console.log('ERROR: User must be authenticated');
      $location.path('/login');
      $scope.user = null;
    }

    /**
     * Used if we want to fetch for pay types, as opposed to using just
     * $scope.payTypes
     */
    $scope.fetchedPayTypes = PayType.find({},
      function (success) {
      });

    $scope.payTypes = [{id: 1, name: "Hourly"}, {id: 2, name: "Salary"}, {id: 3, name: "Stipend"}];

    /**
     * Get company info on-load
     */
    $scope.company = Company.findOne({
        filter: {
          where: {name: $routeParams.companyname},
          include: ['perks', 'majors', 'reviews', {'locations': 'region'}]
        }
      },
      function (successData) {
        $scope.loading.main = false;
      }
    );

    /**
     * Get major info on-load
     */
    $scope.majors = Major.find({},
      function (successData) {
        angular.forEach(successData, function (result) {
          result.title = result.name;
          result.description = result.code;
        });

        $('#majorSearch').search({
          source: $scope.majors,
          maxResults: 4,
          searchFields: ['code', 'name']
        })
      }
    );

    $scope.allPerks = Perk.find({},
      function (successData) {
        angular.forEach(successData, function (value) {
          value['isSelected'] = false;
        });
        $scope.loading.perks = false;
        // Success allperks
      }
    );

    $scope.togglePerkAddition = function (perk) {
      if (!perk.isSelected) {
        perk.isSelected = true; //This toggles ui change
        $scope.formData.perks.push(perk.id); // This adds only id, to formData.perks
      } else {
        $scope.$watch(perk, function (test) {
          console.log('in test');
        });
        console.log('unselecting perk');
        perk.isSelected = false; // this toggles ui change
        var i = $scope.formData.perks.indexOf(perk.id); // this finds and removes perk id from perks
        if (i > -1) {
          $scope.formData.perks.splice(i, 1);
        }
      }
    };

    $scope.formData = {
      overallRating: 0,
      cultureRating: 0,
      difficultyRating: 0,

      description: "",

      pay: "",
      payTypeId: $scope.payTypes[0].id,

      //userId: $scope.user.id == set when async call to USER returns

      perks: [],

      returnOffer: false,
      recommend: false,
      anonymous: true

    };

    /**
     * Actual final SERVICE push to Backend.
     */
    $scope.submitReview = function () {
      $scope.loading.main = true;
      $scope.loading.perks = true;
      console.log("Review is being submitted :) ");
      var pushObject = prepForPush($scope.formData);
      console.log("final submission object is");
      console.log(pushObject);

      Company.reviews.create(
        {id: $scope.company.id},
        pushObject,
        function (success) {
          console.log('success!');

          $scope.loading.main = false;
          $scope.loading.perks = false;
          $location.path('/company/' + $scope.company.name);
        },
        function (error) {
          // TODO handle review creation error
          $scope.loading.main = false;
          $scope.loading.perks = false;
        }
      );

    };

    /**
     * Prepares the formData parameter object to correct keys in order to push to API
     * @param formData
     */
    function prepForPush(formData) {
      // Validate pay input - grabs the pure currency as a string, else null
      if (formData.pay !== "") {
        var isValidPay = formData.pay.search(/^\$?[\d,]+(\.\d*)?$/) >= 0;
        if (isValidPay) {
          formData.pay = formData.pay.replace(/[^0-9\.]/g, '');
        } else {
          formData.pay = null;
        }
      } else {
        formData.pay = null;
      }

      var perksList = [];
      angular.forEach(formData.perks, function (perk) {
        perksList.push(perk);
      });

      var majorsList = [];
      majorsList.push($('#majorSearch').search('get result').id);

      var temploc = $('#locationSearch').search('get result').id;

      var pushObj = {
        anonymous: formData.anonymous,
        returnOffer: formData.returnOffer,
        recommend: formData.recommend,
        description: formData.description,
        overallRating: formData.overallRating,
        cultureRating: formData.cultureRating,
        difficultyRating: formData.cultureRating,
        pay: formData.pay,
        userId: formData.userId,
        payTypeId: formData.payTypeId,

        perks: perksList,
        majors: majorsList,
        location: temploc
      };

      return pushObj;
    }

    /**
     * Wizard variables.
     * @type {{currStep: string, s1: string, s2: string, s3: string}}
     */
    $scope.wizard = {
      currStep: "wizardStepOne",
      s1: "wizardStepOne",
      s2: "wizardStepTwo",
      s3: "wizardStepThree"
    };

    // On document ready, wait half a second and to init Semantic UI elements.
    angular.element(document).ready(function () {
      // Delay to wait for angular to
      setTimeout(setupSemantic, 100);
    });

    /**
     * Runs once on document ready. Set up Semantic UI elements
     */
    function setupSemantic() {
      $('.ui.accordion').accordion();
      $('.ui.selection.dropdown').dropdown();
      $('#payType').dropdown();
      $('.rating').rating({
        /* see http://bit.ly/1M0OaL9 of why we need to do this */
        onRate: function (val) {
          $scope.$apply(function () {
            var ratings = $('.rating').rating('get rating');
            $scope.formData.overallRating = ratings[0];
            $scope.formData.cultureRating = ratings[1];
            $scope.formData.difficultyRating = ratings[2];
          });
        }
      });
      $('#privacyCheckbox').checkbox({
        onChange: function () {
          /* see http://bit.ly/1M0OaL9 of why we need to do this */
          $scope.$apply(function () {
            $scope.formData.anonymous = !$scope.formData.anonymous;
          });
        }
      });
      $('#returnOfferCheckbox').checkbox({
        onChange: function () {
          /* see http://bit.ly/1M0OaL9 of why we need to do this */
          $scope.$apply(function () {
            $scope.formData.returnOffer = !$scope.formData.returnOffer;
          });
        }
      });
      $('#recommendCheckbox').checkbox({
        onChange: function () {
          /* see http://bit.ly/1M0OaL9 of why we need to do this */
          $scope.$apply(function () {
            $scope.formData.recommend = !$scope.formData.recommend;
          });
        }
      });

      $('#locationSearch').search({
        apiSettings: {
          //TODO: not hardcode url, use another system for searching
          url: "https://ratemycoop.io/api/v1/Cities/search?query={query}"
        },
        searchFullText: false,
        searchFields: ['title'],
        maxResults: 10
      });

      $('#reviewForm').form(
        {
          major: {
            identifier: 'majorSearchInput',
            rules: [{
              type: 'empty',
              prompt: 'Please enter your major'
            }]
          },
          city: {
            identifier: 'locationSearchInput',
            rules: [{
              type: 'empty',
              prompt: 'Please enter your major'
            }]
          },
          pay: {
            identifier: 'payInput',
            rules: [
              {
                type: 'empty',
                prompt: 'Please enter a valid pay'
              },
              {
                type: 'length[2]'
              }
            ]
          },
          payType: {
            identifier: 'payTypeInput',
            prompt: 'prompt'
          }
        },
        {
          on: 'blur'
        }
      );
    }

  });
<|endoftext|>


<commit_before>var url = require("url"), util = require('util'), fs = require('fs'), utils = require('../core/utils');
var marked = require('../../public/js/marked.min.js');
var hljs = require('highlight.js');
var mustache = require('mustache');
var PAGE_SIZE = 100

module.exports.controller = function(app) {

	var m = require("../model/BookmarkModel")(app);
	var mTag = require("../model/TagModel")(app);
	var config = require('config');
	var request = require('request')

	app.put('/api/bookmark', function(req, res) {
		app.db.serialize(function(){
			app.db.exec("BEGIN");
			m.insertBookmark(req.body, function(err){
				if(err){
					app.log("m=insertBookmark, status=error, err=%j", err);
					app.em._500({
						message: "Não foi possível cadastrar o bookmark",
						stacktrace: err,
						res: res
					});
					m.rollback();
					return ;
				}
				app.log("bookmark cadastrado, associando as tags..");
				associarTagsBookmarks(app,req, res, this.lastID);
			});
		});
	});

	app.get('/api/memory', function(req, res){
		var m = process.memoryUsage();
		//console.log(m);
		mbytes = (m.heapUsed / 1024.0 / 1024.0);
		res.end(mbytes + '');
	});

	app.delete('/api/bookmark', function(req, res) {
		app.db.serialize(function(){
			app.db.exec("BEGIN");
			app.c.info("m=delete, status=begin, bookmark=%s", req.body.id);
			m.deleteBookmark(req.body.id, function(err){
				if(err){
					app.em._500({
						res: res,
						stacktrace: err,
						message: "Não foi possível deletar o bookmark"
					})
					m.rollback();
				}else{
					app.c.info("m=delete, status=success, bookmark=%s", req.body.id);
					app.db.exec("COMMIT");
					res.end();
				}
			});
		});
	});

	app.post('/api/bookmark', function(req, res) {

		if(!req.body.id || !parseInt(req.body.id) > 0){
			res.writeHead(500);
			res.end("informe o id");
			 return ;
		}
		var id = req.body.id;
		app.db.serialize(function(){
			app.db.exec("BEGIN");
			m.updateBookmark(req.body, function(err){
				if(err){
					app.log("m=updateBookmark, status=error, err=%j", err);
					app.em._500({
						message: "Não foi possível atualizar o bookmark",
						stacktrace: err,
						res: res
					});
					m.rollback();
					return ;
				}
				console.log("bookmark atualizado")
				associarTagsBookmarks(app,req, res, id, true);
			});
		});
	});

	app.post('/api/bookmark/recover', function(req, res) {
		var id;
		if((id=parseInt(req.body.id)) < 0 || Number.isNaN(id)){
			app.em._400({
				message: "É necessário o id do bookmark para efetuar a restauração",
				res: res
			});
			return ;
		}
		app.db.serialize(function(){
			try{
				app.db.exec("BEGIN");
				m.recoverBookmark(id);
				app.db.exec("COMMIT");
				res.end();
			}catch(e){
				app.em._500({
					message: "Não foi possível recuperar o bookmark",
					stacktrace: e.stack,
					res: res
				});
				m.rollback();
			}
		});
	});

	app.get('/api/bookmark', function(req, res) {

		var from = url.parse(req.url, true).query.indice || 0;
		var apiURL = config.get('api.url');

		request(apiURL + '/api/v1.0/bookmark?from=' + from + '&quantity=' + PAGE_SIZE, function (err, response, body) {
			console.debug('M=GET /api/bookmark, error=%s, code=%d', err, response.statusCode);
			if(err != null){
				app.em._500({
					res: res,
					message: "Could not get bookmarks temporally",
					stacktrace: err
				});
				return ;
			}else if(response.statusCode != 200){
				app.em._500({
					res: res,
					message: body.message,
					stacktrace: err
				});
			} else {
				res.header('Content-Type', 'application/json')
				res.send(body);
			}
		});

	});

	app.get('/api/bookmark/search', function(req, res) {

		var query = url.parse(req.url, true).query;
		var apiURL = config.get('api.url');
		var restURL = util.format(
			'%s/api/v1.0/bookmark?from=%d&quantity=%d&tag=%s&query=%s',
			 apiURL, query.indice || 0, PAGE_SIZE, query.tag || '', query.query || ''
		);

		request.get({url: restURL, json: true}, function (err, response, body) {
			console.debug('M=GET /api/bookmark/search, error=%s, code=%d', err, response && response.statusCode);
			if(err != null || response.statusCode != 200){
				console.error('M=GET /api/bookmark/search, status=error, error=%s, code=%d, body=%j', err, response && response.statusCode, body);
				app.em._400({
					res: res,
					message: (body && body.message) || "Could not get bookmarks temporally",
					stacktrace: err
				});
			} else {
				res.header('Content-Type', 'application/json')
				res.send(body);
			}
		});
	});

	app.get('/api/bookmark/:id', function(req, res) {
		m.getBookmarkById(req.params.id, function(err, data){
			res.send(data);
		});
	});

	app.get('/bookmark/new', function(req, res) {
		res.render('bookmarkEdit', {
			layout: false,
			stringify: function(){
				return function(){
					return '[]';
				}
			},
			getVisibilityFlag: getVisibilityFlag
		});
	});

	/**
	 * Public bookmark view
	 */
	app.get('/bookmark/:id/:description', function(req, res) {
		console.info('m=/bookmark/:id/:description, status=begin, id=%s, desc=%s', req.params.id, req.params.description);
		m.getBookmarkByIdWithNavigation(req.params.id, (err, bookmark) => {

			console.debug("M=getPublicBookmark, status=loaded, bookmark=%j", bookmark.bookmark.id);
			var content, title, id;
			if(bookmark.bookmark != null){
					id = bookmark.bookmark.id;
					title = bookmark.bookmark.name;

					// if you make changes here probably you also want to change  public/js/ct/bookmarkEdit.js#267
					content = parseCode(`

{{{code}}}

`, bookmark.bookmark.html);
			}else{
				title = util.format("Bookmark '%s' not found", req.params.description || req.params.id);
				content = "";
			}

			res.render('bookmarkView', {
				analytics: config.get('analytics.id'),
				creationDate: toSQLDate(bookmark.bookmark.creationDate),
				updateDate: toSQLDate(bookmark.bookmark.updateDate),
				prev: bookmark.prev,
				next: bookmark.next,
				layout: 'publicLayout',
				id: id,
				title: title,
				content: content,
				description: utils.clearHTML(content).replace(/[\r\n]+/, "").substring(0, 160),
				encode: function(){
					return function(name, parser){
						return encodeURIComponent(parser(name));
					}
				},
				getURL(){
					return function(path, render){
						return utils.getSEOURL(req, render(path))
					};
				}
			});

		});
	});

	app.get('/bookmark/edit', function(req, res) {
		var query = url.parse(req.url, true).query;
		m.getBookmarkById(query.id, function(err, bookmark){
			mTag.getTagsByBoomarkId(query.id, function(err, tags){
				res.render('bookmarkEdit', {
					layout: false,
					bookmark: bookmark,
					tags: tags,
					editMode: query.editMode,
					stringify: function(){
						return function(val, render){
							return JSON.stringify(this.tags.map(function(tag){
								return tag.name;
							}));
						}
					},
					getVisibilityFlag: getVisibilityFlag
				});
			});
		});
	});

	function associarTagsBookmarks(app,req, res,id, editMode){
		if(editMode){
			try{
				app.log("m=associarTagsBookmarks, status=deletando tags antigas, id=%d, editMode=%s", id, editMode);
				m.dessociateTagsToBookmarkById(id);
			}catch(e){
				app.em._500({
					message: "Não foi possível desassociar o bookmark com as tags",
					res: res,
					stacktrace: e.stack
				});
				m.rollback();
				return ;
			}
		}
		if(!Array.isArray(req.body.tag)){
			app.c.debug("Sem tags para serem cadastradas");
			res.end(id + '');
			app.db.exec("COMMIT");
			return ;
		}
		app.log("existem tags para serem cadastradas");
		var tagsToCad = toTags(req.body.tag);
		app.log("cadastrando tags que ainda não o foram..");
		mTag.mergeTag(tagsToCad, function(err, tags){
			if(err){
				console.error('m=mergeTag, error=%s', err);
				app.em._500({
					message: "Não foi possível cadastrar as novas tags",
					res: res,
					stacktrace: err
				});
				m.rollback();
				return;
	  	}
			app.log("tags foram cadastradas, as relacionadas a esse bookmark são...", tags.map(t => t.slug));
			app.log("associando as novas");
			m.associateTagsToBookmarkById(function(err){
				if(err){
					console.error('m=associateTagsToBookmarkById, error=%s', err);
					app.em._500({
						message: "Não foi possível associar o bookmark com as tags",
						res: res,
						stacktrace: err
					});
					m.rollback();
					return ;
				}
				try{
					app.db.exec("COMMIT");
				}catch(e){
					app.em._500({
						message: "Não foi possível efetuar o cadastro do bookmark, tente novamente mais tarde" ,
						res: res,
						stacktrace: e
					});
					app.db.rollback();
				}
				res.end(id + '');
			}, id, tags);
	  });
	}
};

var languagesMap = {};
hljs.listLanguages().forEach(lang => languagesMap[lang] = true)
function parseCode(template, content){
	var renderer = new marked.Renderer();
	renderer.code = function(code, lang){
		var parsedCode = languagesMap[lang] ? hljs.highlight(lang, code) : hljs.highlightAuto(code);
		return mustache.render(template, {lang: lang, code: parsedCode.value, overflown: parsedCode.value.split(/\r\n|\r|\n/).length > 7 });
	}
	renderer.table = function(header, body) {
		return '
\n'
			+ '
\n'
			+ header
			+ '
\n'
			+ '
\n'
			+ body
			+ '
\n'
			+ '
\n';
	}
	return marked(content || '', {
		renderer: renderer
	})
}

function toTags(tags){
	return tags.map(function(tagName){
		return {
			name: tagName
		};
	});
}

function getVisibilityFlag(){
	return function(number, render){
		number = render(number);
		console.debug('m=getVisibilityFlag, number=%s', number);
		if(number == 1){
			return " checked";
		}
		return "";
	}
}

function toSQLDate(d){
	if(d){
		return d.substring(0, d.length-3)
	}
	return ''
}
<commit_msg>MG-40 fixing npe at log<commit_after>var url = require("url"), util = require('util'), fs = require('fs'), utils = require('../core/utils');
var marked = require('../../public/js/marked.min.js');
var hljs = require('highlight.js');
var mustache = require('mustache');
var PAGE_SIZE = 100

module.exports.controller = function(app) {

	var m = require("../model/BookmarkModel")(app);
	var mTag = require("../model/TagModel")(app);
	var config = require('config');
	var request = require('request')

	app.put('/api/bookmark', function(req, res) {
		app.db.serialize(function(){
			app.db.exec("BEGIN");
			m.insertBookmark(req.body, function(err){
				if(err){
					app.log("m=insertBookmark, status=error, err=%j", err);
					app.em._500({
						message: "Não foi possível cadastrar o bookmark",
						stacktrace: err,
						res: res
					});
					m.rollback();
					return ;
				}
				app.log("bookmark cadastrado, associando as tags..");
				associarTagsBookmarks(app,req, res, this.lastID);
			});
		});
	});

	app.get('/api/memory', function(req, res){
		var m = process.memoryUsage();
		//console.log(m);
		mbytes = (m.heapUsed / 1024.0 / 1024.0);
		res.end(mbytes + '');
	});

	app.delete('/api/bookmark', function(req, res) {
		app.db.serialize(function(){
			app.db.exec("BEGIN");
			app.c.info("m=delete, status=begin, bookmark=%s", req.body.id);
			m.deleteBookmark(req.body.id, function(err){
				if(err){
					app.em._500({
						res: res,
						stacktrace: err,
						message: "Não foi possível deletar o bookmark"
					})
					m.rollback();
				}else{
					app.c.info("m=delete, status=success, bookmark=%s", req.body.id);
					app.db.exec("COMMIT");
					res.end();
				}
			});
		});
	});

	app.post('/api/bookmark', function(req, res) {

		if(!req.body.id || !parseInt(req.body.id) > 0){
			res.writeHead(500);
			res.end("informe o id");
			 return ;
		}
		var id = req.body.id;
		app.db.serialize(function(){
			app.db.exec("BEGIN");
			m.updateBookmark(req.body, function(err){
				if(err){
					app.log("m=updateBookmark, status=error, err=%j", err);
					app.em._500({
						message: "Não foi possível atualizar o bookmark",
						stacktrace: err,
						res: res
					});
					m.rollback();
					return ;
				}
				console.log("bookmark atualizado")
				associarTagsBookmarks(app,req, res, id, true);
			});
		});
	});

	app.post('/api/bookmark/recover', function(req, res) {
		var id;
		if((id=parseInt(req.body.id)) < 0 || Number.isNaN(id)){
			app.em._400({
				message: "É necessário o id do bookmark para efetuar a restauração",
				res: res
			});
			return ;
		}
		app.db.serialize(function(){
			try{
				app.db.exec("BEGIN");
				m.recoverBookmark(id);
				app.db.exec("COMMIT");
				res.end();
			}catch(e){
				app.em._500({
					message: "Não foi possível recuperar o bookmark",
					stacktrace: e.stack,
					res: res
				});
				m.rollback();
			}
		});
	});

	app.get('/api/bookmark', function(req, res) {

		var from = url.parse(req.url, true).query.indice || 0;
		var apiURL = config.get('api.url');

		request(apiURL + '/api/v1.0/bookmark?from=' + from + '&quantity=' + PAGE_SIZE, function (err, response, body) {
			console.info('M=GET /api/bookmark, error=%s, code=%d', err, response.statusCode);
			if(err != null){
				app.em._500({
					res: res,
					message: "Could not get bookmarks temporally",
					stacktrace: err
				});
				return ;
			}else if(response.statusCode != 200){
				app.em._500({
					res: res,
					message: body.message,
					stacktrace: err
				});
			} else {
				res.header('Content-Type', 'application/json')
				res.send(body);
			}
		});

	});

	app.get('/api/bookmark/search', function(req, res) {

		var query = url.parse(req.url, true).query;
		var apiURL = config.get('api.url');
		var restURL = util.format(
			'%s/api/v1.0/bookmark?from=%d&quantity=%d&tag=%s&query=%s',
			 apiURL, query.indice || 0, PAGE_SIZE, query.tag || '', query.query || ''
		);

		request.get({url: restURL, json: true}, function (err, response, body) {
			console.info('M=GET /api/bookmark/search, error=%s, code=%d', err, response && response.statusCode);
			if(err != null || response.statusCode != 200){
				console.error('M=GET /api/bookmark/search, status=error, error=%s, code=%d, body=%j', err, response && response.statusCode, body);
				app.em._400({
					res: res,
					message: (body && body.message) || "Could not get bookmarks temporally",
					stacktrace: err
				});
			} else {
				res.header('Content-Type', 'application/json')
				res.send(body);
			}
		});
	});

	app.get('/api/bookmark/:id', function(req, res) {
		m.getBookmarkById(req.params.id, function(err, data){
			res.send(data);
		});
	});

	app.get('/bookmark/new', function(req, res) {
		res.render('bookmarkEdit', {
			layout: false,
			stringify: function(){
				return function(){
					return '[]';
				}
			},
			getVisibilityFlag: getVisibilityFlag
		});
	});

	/**
	 * Public bookmark view
	 */
	app.get('/bookmark/:id/:description', function(req, res) {
		console.info('m=/bookmark/:id/:description, status=begin, id=%s, desc=%s', req.params.id, req.params.description);
		m.getBookmarkByIdWithNavigation(req.params.id, (err, bookmark) => {

			console.debug("M=getPublicBookmark, status=loaded, bookmark=%j", bookmark.bookmark.id);
			var content, title, id;
			if(bookmark.bookmark != null){
					id = bookmark.bookmark.id;
					title = bookmark.bookmark.name;

					// if you make changes here probably you also want to change  public/js/ct/bookmarkEdit.js#267
					content = parseCode(`

{{{code}}}

`, bookmark.bookmark.html);
			}else{
				title = util.format("Bookmark '%s' not found", req.params.description || req.params.id);
				content = "";
			}

			res.render('bookmarkView', {
				analytics: config.get('analytics.id'),
				creationDate: toSQLDate(bookmark.bookmark.creationDate),
				updateDate: toSQLDate(bookmark.bookmark.updateDate),
				prev: bookmark.prev,
				next: bookmark.next,
				layout: 'publicLayout',
				id: id,
				title: title,
				content: content,
				description: utils.clearHTML(content).replace(/[\r\n]+/, "").substring(0, 160),
				encode: function(){
					return function(name, parser){
						return encodeURIComponent(parser(name));
					}
				},
				getURL(){
					return function(path, render){
						return utils.getSEOURL(req, render(path))
					};
				}
			});

		});
	});

	app.get('/bookmark/edit', function(req, res) {
		var query = url.parse(req.url, true).query;
		m.getBookmarkById(query.id, function(err, bookmark){
			mTag.getTagsByBoomarkId(query.id, function(err, tags){
				res.render('bookmarkEdit', {
					layout: false,
					bookmark: bookmark,
					tags: tags,
					editMode: query.editMode,
					stringify: function(){
						return function(val, render){
							return JSON.stringify(this.tags.map(function(tag){
								return tag.name;
							}));
						}
					},
					getVisibilityFlag: getVisibilityFlag
				});
			});
		});
	});

	function associarTagsBookmarks(app,req, res,id, editMode){
		if(editMode){
			try{
				app.log("m=associarTagsBookmarks, status=deletando tags antigas, id=%d, editMode=%s", id, editMode);
				m.dessociateTagsToBookmarkById(id);
			}catch(e){
				app.em._500({
					message: "Não foi possível desassociar o bookmark com as tags",
					res: res,
					stacktrace: e.stack
				});
				m.rollback();
				return ;
			}
		}
		if(!Array.isArray(req.body.tag)){
			app.c.debug("Sem tags para serem cadastradas");
			res.end(id + '');
			app.db.exec("COMMIT");
			return ;
		}
		app.log("existem tags para serem cadastradas");
		var tagsToCad = toTags(req.body.tag);
		app.log("cadastrando tags que ainda não o foram..");
		mTag.mergeTag(tagsToCad, function(err, tags){
			if(err){
				console.error('m=mergeTag, error=%s', err);
				app.em._500({
					message: "Não foi possível cadastrar as novas tags",
					res: res,
					stacktrace: err
				});
				m.rollback();
				return;
	  	}
			app.log("tags foram cadastradas, as relacionadas a esse bookmark são...", tags.map(t => t.slug));
			app.log("associando as novas");
			m.associateTagsToBookmarkById(function(err){
				if(err){
					console.error('m=associateTagsToBookmarkById, error=%s', err);
					app.em._500({
						message: "Não foi possível associar o bookmark com as tags",
						res: res,
						stacktrace: err
					});
					m.rollback();
					return ;
				}
				try{
					app.db.exec("COMMIT");
				}catch(e){
					app.em._500({
						message: "Não foi possível efetuar o cadastro do bookmark, tente novamente mais tarde" ,
						res: res,
						stacktrace: e
					});
					app.db.rollback();
				}
				res.end(id + '');
			}, id, tags);
	  });
	}
};

var languagesMap = {};
hljs.listLanguages().forEach(lang => languagesMap[lang] = true)
function parseCode(template, content){
	var renderer = new marked.Renderer();
	renderer.code = function(code, lang){
		var parsedCode = languagesMap[lang] ? hljs.highlight(lang, code) : hljs.highlightAuto(code);
		return mustache.render(template, {lang: lang, code: parsedCode.value, overflown: parsedCode.value.split(/\r\n|\r|\n/).length > 7 });
	}
	renderer.table = function(header, body) {
		return '
\n'
			+ '
\n'
			+ header
			+ '
\n'
			+ '
\n'
			+ body
			+ '
\n'
			+ '
\n';
	}
	return marked(content || '', {
		renderer: renderer
	})
}

function toTags(tags){
	return tags.map(function(tagName){
		return {
			name: tagName
		};
	});
}

function getVisibilityFlag(){
	return function(number, render){
		number = render(number);
		console.debug('m=getVisibilityFlag, number=%s', number);
		if(number == 1){
			return " checked";
		}
		return "";
	}
}

function toSQLDate(d){
	if(d){
		return d.substring(0, d.length-3)
	}
	return ''
}
<|endoftext|>