<commit_before>angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])

.constant('dropdownConfig', {
  openClass: 'open'
})

.service('dropdownService', ['$document', '$rootScope', function($document, $rootScope) {
  var openScope = null;

  this.open = function(dropdownScope) {
    if (!openScope) {
      $document.bind('click', closeDropdown);
      $document.bind('keydown', keybindFilter);
    }

    if (openScope && openScope !== dropdownScope) {
      openScope.isOpen = false;
    }

    openScope = dropdownScope;
  };

  this.close = function(dropdownScope) {
    if (openScope === dropdownScope) {
      openScope = null;
      $document.unbind('click', closeDropdown);
      $document.unbind('keydown', keybindFilter);
    }
  };

  var closeDropdown = function(evt) {
    // This method may still be called during the same mouse event that
    // unbound this event handler. So check openScope before proceeding.
    if (!openScope) { return; }

    if (evt && openScope.getAutoClose() === 'disabled')  { return ; }

    var toggleElement = openScope.getToggleElement();
    if (evt && toggleElement && toggleElement[0].contains(evt.target)) {
      return;
    }

    var dropdownElement = openScope.getDropdownElement();
    if (evt && openScope.getAutoClose() === 'outsideClick' &&
      dropdownElement && dropdownElement[0].contains(evt.target)) {
      return;
    }

    openScope.isOpen = false;

    if (!$rootScope.$$phase) {
      openScope.$apply();
    }
  };

  var keybindFilter = function(evt) {
    if (evt.which === 27) {
      openScope.focusToggleElement();
      closeDropdown();
    } else if (openScope.isKeynavEnabled() && /(38|40)/.test(evt.which) && openScope.isOpen) {
      evt.preventDefault();
      evt.stopPropagation();
      openScope.focusDropdownEntry(evt.which);
    }
  };
}])

.controller('DropdownController', ['$scope', '$attrs', '$parse', 'dropdownConfig', 'dropdownService', '$animate', '$position', '$document', '$compile', '$templateRequest', function($scope, $attrs, $parse, dropdownConfig, dropdownService, $animate, $position, $document, $compile, $templateRequest) {
  var self = this,
    scope = $scope.$new(), // create a child scope so we are not polluting original one
    templateScope,
    openClass = dropdownConfig.openClass,
    getIsOpen,
    setIsOpen = angular.noop,
    toggleInvoker = $attrs.onToggle ? $parse($attrs.onToggle) : angular.noop,
    appendToBody = false,
    keynavEnabled = false,
    selectedOption = null,
    body = $document.find('body');

  this.init = function(element) {
    self.$element = element;

    if ($attrs.isOpen) {
      getIsOpen = $parse($attrs.isOpen);
      setIsOpen = getIsOpen.assign;

      $scope.$watch(getIsOpen, function(value) {
        scope.isOpen = !!value;
      });
    }

    appendToBody = scope.$eval($attrs.dropdownAppendToBody) || false;
    keynavEnabled = angular.isDefined($attrs.keyboardNav);

    if (appendToBody && self.dropdownMenu) {
      body.append(self.dropdownMenu);
      body.addClass('dropdown');
      element.on('$destroy', function handleDestroyEvent() {
        self.dropdownMenu.remove();
      });
    }
  };

  this.toggle = function(open) {
    return scope.isOpen = arguments.length ? !!open : !scope.isOpen;
  };

  // Allow other directives to watch status
  this.isOpen = function() {
    return scope.isOpen;
  };

  scope.getToggleElement = function() {
    return self.toggleElement;
  };

  scope.getAutoClose = function() {
    return $attrs.autoClose || 'always'; //or 'outsideClick' or 'disabled'
  };

  scope.getElement = function() {
    return self.$element;
  };

  scope.isKeynavEnabled = function() {
    return keynavEnabled;
  };

  scope.focusDropdownEntry = function(keyCode) {
    var elems = self.dropdownMenu ? //If append to body is used.
      (angular.element(self.dropdownMenu).find('a')) :
      (angular.element(self.$element).find('ul').eq(0).find('a'));

    switch (keyCode) {
      case (40): {
        if (!angular.isNumber(self.selectedOption)) {
          self.selectedOption = 0;
        } else {
          self.selectedOption = (self.selectedOption === elems.length -1 ?
            self.selectedOption :
            self.selectedOption + 1);
        }
        break;
      }
      case (38): {
        if (!angular.isNumber(self.selectedOption)) {
          self.selectedOption = elems.length - 1;
        } else {
          self.selectedOption = self.selectedOption === 0 ?
            0 : self.selectedOption - 1;
        }
        break;
      }
    }
    elems[self.selectedOption].focus();
  };

  scope.getDropdownElement = function() {
    return self.dropdownMenu;
  };

  scope.focusToggleElement = function() {
    if (self.toggleElement) {
      self.toggleElement[0].focus();
    }
  };

  scope.$watch('isOpen', function(isOpen, wasOpen) {
    if (appendToBody && self.dropdownMenu) {
      var pos = $position.positionElements(self.$element, self.dropdownMenu, 'bottom-left', true);
      var css = {
        top: pos.top + 'px',
        display: isOpen ? 'block' : 'none'
      };

      // Check if there's enough space for dropdownMenu to position below the element
      // and if not position it above
      var documentHeight = $document.innerHeight();
      var dropdownMenuHeight = self.dropdownMenu.height();
      var elementHeight = self.$element.height();
      if (pos.top + dropdownMenuHeight > documentHeight) {
        css.top = pos.top - (elementHeight + dropdownMenuHeight) + 'px';
        self.dropdownMenu.addClass('dropdown-menu-top');
      }
      else {
        self.dropdownMenu.removeClass('dropdown-menu-top');
      }

      var rightalign = self.dropdownMenu.hasClass('dropdown-menu-right');
      if (!rightalign) {
        css.left = pos.left + 'px';
        css.right = 'auto';
      } else {
        css.left = 'auto';
        css.right = (window.innerWidth - (pos.left + self.$element.prop('offsetWidth'))) + 'px';
      }

      self.dropdownMenu.css(css);
    }

    var openContainer = appendToBody ? body : self.$element;

    $animate[isOpen ? 'addClass' : 'removeClass'](openContainer, openClass).then(function() {
      if (angular.isDefined(isOpen) && isOpen !== wasOpen) {
        toggleInvoker($scope, { open: !!isOpen });
      }
    });

    if (isOpen) {
      if (self.dropdownMenuTemplateUrl) {
        $templateRequest(self.dropdownMenuTemplateUrl).then(function(tplContent) {
          templateScope = scope.$new();
          $compile(tplContent.trim())(templateScope, function(dropdownElement) {
            var newEl = dropdownElement;
            self.dropdownMenu.replaceWith(newEl);
            self.dropdownMenu = newEl;
          });
        });
      }

      scope.focusToggleElement();
      dropdownService.open(scope);
    } else {
      if (self.dropdownMenuTemplateUrl) {
        if (templateScope) {
          templateScope.$destroy();
        }
        var newEl = angular.element('

');
        self.dropdownMenu.replaceWith(newEl);
        self.dropdownMenu = newEl;
      }

      dropdownService.close(scope);
      self.selectedOption = null;
    }

    if (angular.isFunction(setIsOpen)) {
      setIsOpen($scope, isOpen);
    }
  });

  $scope.$on('$locationChangeSuccess', function() {
    if (scope.getAutoClose() !== 'disabled') {
      scope.isOpen = false;
    }
  });

  var offDestroy = $scope.$on('$destroy', function() {
    scope.$destroy();
  });
  scope.$on('$destroy', offDestroy);
}])

.directive('dropdown', function() {
  return {
    controller: 'DropdownController',
    link: function(scope, element, attrs, dropdownCtrl) {
      dropdownCtrl.init(element);
      element.addClass('dropdown');
    }
  };
})

.directive('dropdownMenu', function() {
  return {
    restrict: 'AC',
    require: '?^dropdown',
    link: function(scope, element, attrs, dropdownCtrl) {
      if (!dropdownCtrl) {
        return;
      }
      var tplUrl = attrs.templateUrl;
      if (tplUrl) {
        dropdownCtrl.dropdownMenuTemplateUrl = tplUrl;
      }
      if (!dropdownCtrl.dropdownMenu) {
        dropdownCtrl.dropdownMenu = element;
      }
    }
  };
})

.directive('keyboardNav', function() {
  return {
    restrict: 'A',
    require: '?^dropdown',
    link: function(scope, element, attrs, dropdownCtrl) {
      element.bind('keydown', function(e) {
        if ([38, 40].indexOf(e.which) !== -1) {
          e.preventDefault();
          e.stopPropagation();

          var elems = dropdownCtrl.dropdownMenu.find('a');

          switch (e.which) {
            case (40): { // Down
              if (!angular.isNumber(dropdownCtrl.selectedOption)) {
                dropdownCtrl.selectedOption = 0;
              } else {
                dropdownCtrl.selectedOption = dropdownCtrl.selectedOption === elems.length -1 ?
                  dropdownCtrl.selectedOption : dropdownCtrl.selectedOption + 1;
              }
              break;
            }
            case (38): { // Up
              if (!angular.isNumber(dropdownCtrl.selectedOption)) {
                dropdownCtrl.selectedOption = elems.length - 1;
              } else {
                dropdownCtrl.selectedOption = dropdownCtrl.selectedOption === 0 ?
                  0 : dropdownCtrl.selectedOption - 1;
              }
              break;
            }
          }
          elems[dropdownCtrl.selectedOption].focus();
        }
      });
    }
  };
})

.directive('dropdownToggle', function() {
  return {
    require: '?^dropdown',
    link: function(scope, element, attrs, dropdownCtrl) {
      if (!dropdownCtrl) {
        return;
      }

      element.addClass('dropdown-toggle');

      dropdownCtrl.toggleElement = element;

      var toggleDropdown = function(event) {
        event.preventDefault();

        if (!element.hasClass('disabled') && !attrs.disabled) {
          scope.$apply(function() {
            dropdownCtrl.toggle();
          });
        }
      };

      element.bind('click', toggleDropdown);

      // WAI-ARIA
      element.attr({ 'aria-haspopup': true, 'aria-expanded': false });
      scope.$watch(dropdownCtrl.isOpen, function(isOpen) {
        element.attr('aria-expanded', !!isOpen);
      });

      scope.$on('$destroy', function() {
        element.unbind('click', toggleDropdown);
      });
    }
  };
});
<commit_msg>dropdowns<commit_after>angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])

.constant('dropdownConfig', {
  openClass: 'open'
})

.service('dropdownService', ['$document', '$rootScope', function($document, $rootScope) {
  var openScope = null;

  this.open = function(dropdownScope) {
    if (!openScope) {
      $document.bind('click', closeDropdown);
      $document.bind('keydown', keybindFilter);
    }

    if (openScope && openScope !== dropdownScope) {
      openScope.isOpen = false;
    }

    openScope = dropdownScope;
  };

  this.close = function(dropdownScope) {
    if (openScope === dropdownScope) {
      openScope = null;
      $document.unbind('click', closeDropdown);
      $document.unbind('keydown', keybindFilter);
    }
  };

  var closeDropdown = function(evt) {
    // This method may still be called during the same mouse event that
    // unbound this event handler. So check openScope before proceeding.
    if (!openScope) { return; }

    if (evt && openScope.getAutoClose() === 'disabled')  { return ; }

    var toggleElement = openScope.getToggleElement();
    if (evt && toggleElement && toggleElement[0].contains(evt.target)) {
      return;
    }

    var dropdownElement = openScope.getDropdownElement();
    if (evt && openScope.getAutoClose() === 'outsideClick' &&
      dropdownElement && dropdownElement[0].contains(evt.target)) {
      return;
    }

    openScope.isOpen = false;

    if (!$rootScope.$$phase) {
      openScope.$apply();
    }
  };

  var keybindFilter = function(evt) {
    if (evt.which === 27) {
      openScope.focusToggleElement();
      closeDropdown();
    } else if (openScope.isKeynavEnabled() && /(38|40)/.test(evt.which) && openScope.isOpen) {
      evt.preventDefault();
      evt.stopPropagation();
      openScope.focusDropdownEntry(evt.which);
    }
  };
}])

.controller('DropdownController', ['$scope', '$attrs', '$parse', 'dropdownConfig', 'dropdownService', '$animate', '$position', '$document', '$compile', '$templateRequest', function($scope, $attrs, $parse, dropdownConfig, dropdownService, $animate, $position, $document, $compile, $templateRequest) {
  var self = this,
    scope = $scope.$new(), // create a child scope so we are not polluting original one
    templateScope,
    openClass = dropdownConfig.openClass,
    getIsOpen,
    setIsOpen = angular.noop,
    toggleInvoker = $attrs.onToggle ? $parse($attrs.onToggle) : angular.noop,
    appendToBody = false,
    keynavEnabled = false,
    selectedOption = null,
    body = $document.find('body');

  this.init = function(element) {
    self.$element = element;

    if ($attrs.isOpen) {
      getIsOpen = $parse($attrs.isOpen);
      setIsOpen = getIsOpen.assign;

      $scope.$watch(getIsOpen, function(value) {
        scope.isOpen = !!value;
      });
    }

    appendToBody = scope.$eval($attrs.dropdownAppendToBody) || false;
    keynavEnabled = angular.isDefined($attrs.keyboardNav);

    if (appendToBody && self.dropdownMenu) {
      body.append(self.dropdownMenu);
      body.addClass('dropdown');
      element.on('$destroy', function handleDestroyEvent() {
        self.dropdownMenu.remove();
      });
    }
  };

  this.toggle = function(open) {
    return scope.isOpen = arguments.length ? !!open : !scope.isOpen;
  };

  // Allow other directives to watch status
  this.isOpen = function() {
    return scope.isOpen;
  };

  scope.getToggleElement = function() {
    return self.toggleElement;
  };

  scope.getAutoClose = function() {
    return $attrs.autoClose || 'always'; //or 'outsideClick' or 'disabled'
  };

  scope.getElement = function() {
    return self.$element;
  };

  scope.isKeynavEnabled = function() {
    return keynavEnabled;
  };

  scope.focusDropdownEntry = function(keyCode) {
    var elems = self.dropdownMenu ? //If append to body is used.
      (angular.element(self.dropdownMenu).find('a')) :
      (angular.element(self.$element).find('ul').eq(0).find('a'));

    switch (keyCode) {
      case (40): {
        if (!angular.isNumber(self.selectedOption)) {
          self.selectedOption = 0;
        } else {
          self.selectedOption = (self.selectedOption === elems.length -1 ?
            self.selectedOption :
            self.selectedOption + 1);
        }
        break;
      }
      case (38): {
        if (!angular.isNumber(self.selectedOption)) {
          self.selectedOption = elems.length - 1;
        } else {
          self.selectedOption = self.selectedOption === 0 ?
            0 : self.selectedOption - 1;
        }
        break;
      }
    }
    elems[self.selectedOption].focus();
  };

  scope.getDropdownElement = function() {
    return self.dropdownMenu;
  };

  scope.focusToggleElement = function() {
    if (self.toggleElement) {
      self.toggleElement[0].focus();
    }
  };

  scope.$watch('isOpen', function(isOpen, wasOpen) {
    if (appendToBody && self.dropdownMenu) {
      var pos = $position.positionElements(self.$element, self.dropdownMenu, 'bottom-left', true);
      var css = {
        top: pos.top + 'px',
        display: isOpen ? 'block' : 'none'
      };

      // Check if there's enough space for dropdownMenu to position below the element
      // and if not position it above
      var documentHeight = $document.innerHeight();
      var dropdownMenuHeight = self.dropdownMenu.height();
      var elementHeight = self.$element.height();
      if (pos.top + dropdownMenuHeight > documentHeight) {
        css.top = pos.top - (elementHeight + dropdownMenuHeight) + 'px';
        self.dropdownMenu.addClass('dropdown-menu-top');
      }
      else {
        self.dropdownMenu.removeClass('dropdown-menu-top');
      }

      var rightalign = self.dropdownMenu.hasClass('dropdown-menu-right');
      if (!rightalign) {
        css.left = pos.left + 'px';
        css.right = 'auto';
      } else {
        css.left = 'auto';
        css.right = (window.innerWidth - (pos.left + self.$element.prop('offsetWidth'))) + 'px';
      }

      self.dropdownMenu.css(css);
    }

    var openContainer = appendToBody ? body : self.$element;

    $animate[isOpen ? 'addClass' : 'removeClass'](openContainer, openClass).then(function() {
      if (angular.isDefined(isOpen) && isOpen !== wasOpen) {
        toggleInvoker($scope, { open: !!isOpen });
      }
    });

    if (isOpen) {
      if (self.dropdownMenuTemplateUrl) {
        $templateRequest(self.dropdownMenuTemplateUrl).then(function(tplContent) {
          templateScope = scope.$new();
          $compile(tplContent.trim())(templateScope, function(dropdownElement) {
            var newEl = dropdownElement;
            self.dropdownMenu.replaceWith(newEl);
            self.dropdownMenu = newEl;
          });
        });
      }

      scope.focusToggleElement();
      dropdownService.open(scope);
      // hide dropdownMenu on scroll
      if (appendToBody && self.dropdownMenu) {
        document.addEventListener('nanoscroll', function () {
          self.dropdownMenu.css({display: 'none'});
          scope.isOpen = false;
        });
      }
    } else {
      if (self.dropdownMenuTemplateUrl) {
        if (templateScope) {
          templateScope.$destroy();
        }
        var newEl = angular.element('

');
        self.dropdownMenu.replaceWith(newEl);
        self.dropdownMenu = newEl;
      }

      dropdownService.close(scope);
      self.selectedOption = null;

      document.removeEventListener('nanoscroll');
    }

    if (angular.isFunction(setIsOpen)) {
      setIsOpen($scope, isOpen);
    }
  });

  $scope.$on('$locationChangeSuccess', function() {
    if (scope.getAutoClose() !== 'disabled') {
      scope.isOpen = false;
    }
  });

  var offDestroy = $scope.$on('$destroy', function() {
    scope.$destroy();
  });
  scope.$on('$destroy', offDestroy);
}])

.directive('dropdown', function() {
  return {
    controller: 'DropdownController',
    link: function(scope, element, attrs, dropdownCtrl) {
      dropdownCtrl.init(element);
      element.addClass('dropdown');
    }
  };
})

.directive('dropdownMenu', function() {
  return {
    restrict: 'AC',
    require: '?^dropdown',
    link: function(scope, element, attrs, dropdownCtrl) {
      if (!dropdownCtrl) {
        return;
      }
      var tplUrl = attrs.templateUrl;
      if (tplUrl) {
        dropdownCtrl.dropdownMenuTemplateUrl = tplUrl;
      }
      if (!dropdownCtrl.dropdownMenu) {
        dropdownCtrl.dropdownMenu = element;
      }
    }
  };
})

.directive('keyboardNav', function() {
  return {
    restrict: 'A',
    require: '?^dropdown',
    link: function(scope, element, attrs, dropdownCtrl) {
      element.bind('keydown', function(e) {
        if ([38, 40].indexOf(e.which) !== -1) {
          e.preventDefault();
          e.stopPropagation();

          var elems = dropdownCtrl.dropdownMenu.find('a');

          switch (e.which) {
            case (40): { // Down
              if (!angular.isNumber(dropdownCtrl.selectedOption)) {
                dropdownCtrl.selectedOption = 0;
              } else {
                dropdownCtrl.selectedOption = dropdownCtrl.selectedOption === elems.length -1 ?
                  dropdownCtrl.selectedOption : dropdownCtrl.selectedOption + 1;
              }
              break;
            }
            case (38): { // Up
              if (!angular.isNumber(dropdownCtrl.selectedOption)) {
                dropdownCtrl.selectedOption = elems.length - 1;
              } else {
                dropdownCtrl.selectedOption = dropdownCtrl.selectedOption === 0 ?
                  0 : dropdownCtrl.selectedOption - 1;
              }
              break;
            }
          }
          elems[dropdownCtrl.selectedOption].focus();
        }
      });
    }
  };
})

.directive('dropdownToggle', function() {
  return {
    require: '?^dropdown',
    link: function(scope, element, attrs, dropdownCtrl) {
      if (!dropdownCtrl) {
        return;
      }

      element.addClass('dropdown-toggle');

      dropdownCtrl.toggleElement = element;

      var toggleDropdown = function(event) {
        event.preventDefault();

        if (!element.hasClass('disabled') && !attrs.disabled) {
          scope.$apply(function() {
            dropdownCtrl.toggle();
          });
        }
      };

      element.bind('click', toggleDropdown);

      // WAI-ARIA
      element.attr({ 'aria-haspopup': true, 'aria-expanded': false });
      scope.$watch(dropdownCtrl.isOpen, function(isOpen) {
        element.attr('aria-expanded', !!isOpen);
      });

      scope.$on('$destroy', function() {
        element.unbind('click', toggleDropdown);
      });
    }
  };
});
<|endoftext|>


<commit_before>var ini =
{
  // all files in http-clients must be updated to the cvs version of this file
  protocol_version: '3',
  max_frames: 100,
  debug: false,
  dragonfly_version: '0.7',
  revision_number: '08.04.24.19.05'
}
<commit_msg>commandline, bound the execution scope to the current active.<commit_after>var ini =
{
  // all files in http-clients must be updated to the cvs version of this file
  protocol_version: '3',
  max_frames: 100,
  debug: false,
  dragonfly_version: '0.7',
  revision_number: '08.04.25.16.44'
}
<|endoftext|>


<commit_before>import eventHooks from './shared/eventHooks';

/**
 * Creates a wrapped handler that hooks into the Inferno
 * eventHooks system based on the type of event being
 * attached.
 *
 * @param {string} type
 * @param {function} handler
 * @return {function} wrapped handler
*/
export default function setHandler(type, handler) {
	let wrapper = eventHooks[type];
	if (wrapper) {
		return wrapper(handler);
	}

	return { handler };
}<commit_msg>should be uppercase F<commit_after>import eventHooks from './shared/eventHooks';

/**
 * Creates a wrapped handler that hooks into the Inferno
 * eventHooks system based on the type of event being
 * attached.
 *
 * @param {string} type
 * @param {Function} handler
 * @return {Function} wrapped handler
*/
export default function setHandler(type, handler) {
	let wrapper = eventHooks[type];
	if (wrapper) {
		return wrapper(handler);
	}

	return { handler };
}<|endoftext|>


<commit_before>import forEach from 'lodash/collection/forEach'
import map from 'lodash/collection/map'
import uniq from 'lodash/array/uniq'
import {chain} from 'lodash'

import Reflux from 'reflux'
import Immutable from 'immutable'

import Student from '../models/student'
import demoStudent from '../models/demoStudent.json'

import studentActions from '../flux/studentActions'
import notificationActions from '../flux/notificationActions'

let cleanLocalStorage = () => {
	localStorage.removeItem('activeStudentId')
	localStorage.removeItem('student-v3.0a6')
}

let studentStore = Reflux.createStore({
	listenables: studentActions,

	init() {
		this.students = Immutable.Map()
		this.history = Immutable.Stack()
		this.future = Immutable.Stack()

		this._loadData()
	},

	getInitialState() {
		return this.students
	},

	undo() {
		if (this.history.size) {
			// console.log('undoing...')
			this.future = this.future.push(this.students)
			this.students = this.history.first()
			this.history = this.history.pop()
			this._postChange()
		}
	},

	redo() {
		if (this.future.size) {
			// console.log('redoing...')
			this.history = this.history.push(this.students)
			this.students = this.future.first()
			this.future = this.future.pop()
			this._postChange()
		}
	},

	_preChange() {
		this.history = this.history.push(this.students)
		this.future = this.future.clear()
	},

	_postChange() {
		// console.groupCollapsed('studentStore._postChange')
		// console.group('studentStore._postChange')
		// console.log('students', this.students)
		this.students.forEach(student => student.save())
		// console.groupEnd('studentStore._postChange')
		this.trigger(this.students)
	},

	resetStudentToDemo(studentId) {
		// console.info(`resetting student ${studentId} to the demo student`)
		const rawStudent = demoStudent
		rawStudent.id = studentId

		window.studentData = student
		const student = new Student(rawStudent)

		this._preChange()
		this.students = this.students.set(studentId, student)
		this._postChange()
	},

	reloadStudents() {
		let loadedIds = this.students.map(s => s.id).toList()
		let localIds = Immutable.List(localStorage.getItem('studentIds') || [])

		if (!loadedIds.equals(localIds)) {
			this._loadData()
		}
	},

	_loadData(studentId) {
		// console.log('studentStore._loadData')

		// studentIds is a list of IDs we know about.
		let studentIds =
			// Get the list of students we know about, or the string 'null',
			// if localStorage doesn't have the key 'studentIds'.
			JSON.parse(localStorage.getItem('studentIds')) ||
			// If both those fail, grab the really old 'student-v3.0a6'
			['student-v3.0a6']

		if (studentId) {
			studentIds.push(studentId)
		}

		studentIds = uniq(studentIds)

		// Fetch and load the students from their IDs
		let localStudents = chain(studentIds)
			// pull the students from localStorage
			.map(id => [id, localStorage.getItem(id)])
			// Remove any broken students from localStorage
			.map(([id, rawStudent]) => {
				if (rawStudent === '[object Object]') {
					localStorage.removeItem(id)
				}
				return rawStudent
			})
			// filter out any that don't exist
			.reject(rawStudent => rawStudent === null)
			// and any that are bad
			.reject(rawStudent => rawStudent === '[object Object]')
			// then process them
			.map((rawStudent) => {
				// basicStudent defaults to an empty object so that the constructor
				// has something to build from.
				let basicStudent = {}
				try {
					basicStudent = JSON.parse(rawStudent)
				}
				catch (e) {
					notificationActions.logError('error parsing student', basicStudent)
				}

				if (basicStudent.id === 'student-v3.0a6') {
					delete basicStudent.id
				}

				// Make the student...
				const fleshedStudent = new Student(basicStudent)

				// and save them, of course
				fleshedStudent.save()

				return fleshedStudent
			})
			.value()

		// Update the studentIds list from the current list of students
		localStorage.setItem('studentIds', JSON.stringify(map(localStudents, s => s.id)))

		// Add them to students
		this.students = this.students.withMutations(students => {
			forEach(localStudents, localStudent => {
				students = students.set(localStudent.id, localStudent)
			})
			return students
		})

		// Clean up localStorage
		cleanLocalStorage()

		this._postChange()
	},

	initStudent() {
		const fleshedStudent = new Student()
		fleshedStudent.save()
		this._preChange()
		this._loadData(fleshedStudent.id)
	},

	importStudent(rawStudent) {
		let stu = undefined
		try {
			stu = JSON.parse(rawStudent)
		}
		catch (err) {
			console.error('Error parsing as JSON', rawStudent, err)
		}

		if (stu) {
			this._preChange()
			const fleshedStudent = new Student(stu)
			fleshedStudent.save()
			this._loadData(fleshedStudent.id)
		}
	},

	_change(studentId, method, ...args) {
		this._preChange()
		this.students = this.students.set(studentId, this.students.get(studentId)[method](...args))
		this._postChange()
	},

	_alter(pathToData, method, ...args) {
		this._preChange()
		this.students = this.students.setIn(pathToData, this.students.getIn(pathToData)[method](...args))
		this._postChange()
	},

	/*eslint-disable no-multi-spaces, brace-style */
	changeName(studentId, ...args)               { this._change(studentId, 'changeName',               ...args) },
	changeCreditsNeeded(studentId, ...args)      { this._change(studentId, 'changeCreditsNeeded',      ...args) },
	changeMatriculation(studentId, ...args)      { this._change(studentId, 'changeMatriculation',      ...args) },
	changeGraduation(studentId, ...args)         { this._change(studentId, 'changeGraduation',         ...args) },
	changeSetting(studentId, key, value)         { this._change(studentId, 'changeSetting',         key, value) },
	addArea(studentId, ...args)                  { this._change(studentId, 'addArea',                  ...args) },
	addSchedule(studentId, ...args)              { this._change(studentId, 'addSchedule',              ...args) },
	addFabrication(studentId, ...args)           { this._change(studentId, 'addArea',                  ...args) },
	addOverride(studentId, ...args)              { this._change(studentId, 'addSchedule',              ...args) },
	removeArea(studentId, ...args)               { this._change(studentId, 'removeArea',               ...args) },
	removeMultipleAreas(studentId, ...args)      { this._change(studentId, 'removeMultipleAreas',       ...args) },
	destroySchedule(studentId, ...args)          { this._change(studentId, 'destroySchedule',          ...args) },
	destroyMultipleSchedules(studentId, ...args) { this._change(studentId, 'destroyMultipleSchedules', ...args) },
	moveCourse(studentId, ...args)               { this._change(studentId, 'moveCourse',               ...args) },

	renameSchedule(studentId, scheduleId, ...args)  { this._alter([studentId, 'schedules', scheduleId], 'rename',        ...args) },
	reorderSchedule(studentId, scheduleId, ...args) { this._alter([studentId, 'schedules', scheduleId], 'reorder',       ...args) },
	moveSchedule(studentId, scheduleId, ...args)    { this._alter([studentId, 'schedules', scheduleId], 'move',          ...args) },
	addCourse(studentId, scheduleId, ...args)       { this._alter([studentId, 'schedules', scheduleId], 'addCourse',     ...args) },
	removeCourse(studentId, scheduleId, ...args)    { this._alter([studentId, 'schedules', scheduleId], 'removeCourse',  ...args) },
	reorderCourse(studentId, scheduleId, ...args)   { this._alter([studentId, 'schedules', scheduleId], 'reorderCourse', ...args) },
	reorderArea(studentId, areaId, ...args)         { this._alter([studentId, 'studies', areaId],       'reorder',       ...args) },
	/*eslint-enable no-multi-spaces, brace-style */
})

window.store = studentStore

export default studentStore
<commit_msg>Remove use of lodash.chain<commit_after>import forEach from 'lodash/collection/forEach'
import map from 'lodash/collection/map'
import uniq from 'lodash/array/uniq'
import stringify from 'json-stable-stringify'

import Reflux from 'reflux'
import Immutable from 'immutable'

import Student from '../models/student'
import demoStudent from '../models/demoStudent.json'

import studentActions from '../flux/studentActions'
import notificationActions from '../flux/notificationActions'

let cleanLocalStorage = () => {
	localStorage.removeItem('activeStudentId')
	localStorage.removeItem('student-v3.0a6')
}

let studentStore = Reflux.createStore({
	listenables: studentActions,

	init() {
		this.students = Immutable.Map()
		this.history = Immutable.Stack()
		this.future = Immutable.Stack()

		this._loadData()
	},

	getInitialState() {
		return this.students
	},

	undo() {
		if (this.history.size) {
			// console.log('undoing...')
			this.future = this.future.push(this.students)
			this.students = this.history.first()
			this.history = this.history.pop()
			this._postChange()
		}
	},

	redo() {
		if (this.future.size) {
			// console.log('redoing...')
			this.history = this.history.push(this.students)
			this.students = this.future.first()
			this.future = this.future.pop()
			this._postChange()
		}
	},

	_preChange() {
		this.history = this.history.push(this.students)
		this.future = this.future.clear()
	},

	_postChange() {
		// console.groupCollapsed('studentStore._postChange')
		// console.group('studentStore._postChange')
		// console.log('students', this.students)
		this.students.forEach(student => student.save())
		// console.groupEnd('studentStore._postChange')
		this.trigger(this.students)
	},

	resetStudentToDemo(studentId) {
		// console.info(`resetting student ${studentId} to the demo student`)
		const rawStudent = demoStudent
		rawStudent.id = studentId

		window.studentData = student
		const student = new Student(rawStudent)

		this._preChange()
		this.students = this.students.set(studentId, student)
		this._postChange()
	},

	reloadStudents() {
		let loadedIds = this.students.map(s => s.id).toList()
		let localIds = Immutable.List(localStorage.getItem('studentIds') || [])

		if (!loadedIds.equals(localIds)) {
			this._loadData()
		}
	},

	_loadData(studentId) {
		// console.log('studentStore._loadData')

		// studentIds is a list of IDs we know about.
		let studentIds =
			// Get the list of students we know about, or the string 'null',
			// if localStorage doesn't have the key 'studentIds'.
			JSON.parse(localStorage.getItem('studentIds')) ||
			// If both those fail, grab the really old 'student-v3.0a6'
			['student-v3.0a6']

		if (studentId) {
			studentIds.push(studentId)
		}

		studentIds = uniq(studentIds)

		// Fetch and load the students from their IDs
		const localStudents = Immutable.List(studentIds)
			// pull the students from localStorage
			.map(id => [id, localStorage.getItem(id)])
			// Remove any broken students from localStorage
			.map(([id, rawStudent]) => {
				if (rawStudent === '[object Object]') {
					localStorage.removeItem(id)
				}
				return rawStudent
			})
			// filter out any that don't exist
			.filterNot(rawStudent => rawStudent === null)
			// and any that are bad
			.filterNot(rawStudent => rawStudent === '[object Object]')
			// then process them
			.map(rawStudent => {
				// basicStudent defaults to an empty object so that the constructor
				// has something to build from.
				let basicStudent = {}

				try {
					basicStudent = JSON.parse(rawStudent)
				}
				catch (e) {
					notificationActions.logError('error parsing student', basicStudent)
				}

				if (basicStudent.id === 'student-v3.0a6') {
					delete basicStudent.id
				}

				// Make the student...
				const fleshedStudent = new Student(basicStudent)

				// and save them, of course
				fleshedStudent.save()

				return fleshedStudent
			})

		// Update the studentIds list from the current list of students
		localStorage.setItem('studentIds', stringify(localStudents.map(s => s.id).toArray()))

		// Add them to students
		this.students = this.students.withMutations(students => {
			localStudents.forEach(localStudent => {
				students = students.set(localStudent.id, localStudent)
			})
			return students
		})

		// Clean up localStorage
		cleanLocalStorage()

		this._postChange()
	},

	initStudent() {
		const fleshedStudent = new Student()
		fleshedStudent.save()
		this._preChange()
		this._loadData(fleshedStudent.id)
	},

	importStudent(rawStudent) {
		let stu = undefined
		try {
			stu = JSON.parse(rawStudent)
		}
		catch (err) {
			console.error('Error parsing as JSON', rawStudent, err)
		}

		if (stu) {
			this._preChange()
			const fleshedStudent = new Student(stu)
			fleshedStudent.save()
			this._loadData(fleshedStudent.id)
		}
	},

	_change(studentId, method, ...args) {
		this._preChange()
		this.students = this.students.set(studentId, this.students.get(studentId)[method](...args))
		this._postChange()
	},

	_alter(pathToData, method, ...args) {
		this._preChange()
		this.students = this.students.setIn(pathToData, this.students.getIn(pathToData)[method](...args))
		this._postChange()
	},

	/*eslint-disable no-multi-spaces, brace-style */
	changeName(studentId, ...args)               { this._change(studentId, 'changeName',               ...args) },
	changeCreditsNeeded(studentId, ...args)      { this._change(studentId, 'changeCreditsNeeded',      ...args) },
	changeMatriculation(studentId, ...args)      { this._change(studentId, 'changeMatriculation',      ...args) },
	changeGraduation(studentId, ...args)         { this._change(studentId, 'changeGraduation',         ...args) },
	changeSetting(studentId, key, value)         { this._change(studentId, 'changeSetting',         key, value) },
	addArea(studentId, ...args)                  { this._change(studentId, 'addArea',                  ...args) },
	addSchedule(studentId, ...args)              { this._change(studentId, 'addSchedule',              ...args) },
	addFabrication(studentId, ...args)           { this._change(studentId, 'addArea',                  ...args) },
	addOverride(studentId, ...args)              { this._change(studentId, 'addSchedule',              ...args) },
	removeArea(studentId, ...args)               { this._change(studentId, 'removeArea',               ...args) },
	removeMultipleAreas(studentId, ...args)      { this._change(studentId, 'removeMultipleAreas',       ...args) },
	destroySchedule(studentId, ...args)          { this._change(studentId, 'destroySchedule',          ...args) },
	destroyMultipleSchedules(studentId, ...args) { this._change(studentId, 'destroyMultipleSchedules', ...args) },
	moveCourse(studentId, ...args)               { this._change(studentId, 'moveCourse',               ...args) },

	renameSchedule(studentId, scheduleId, ...args)  { this._alter([studentId, 'schedules', scheduleId], 'rename',        ...args) },
	reorderSchedule(studentId, scheduleId, ...args) { this._alter([studentId, 'schedules', scheduleId], 'reorder',       ...args) },
	moveSchedule(studentId, scheduleId, ...args)    { this._alter([studentId, 'schedules', scheduleId], 'move',          ...args) },
	addCourse(studentId, scheduleId, ...args)       { this._alter([studentId, 'schedules', scheduleId], 'addCourse',     ...args) },
	removeCourse(studentId, scheduleId, ...args)    { this._alter([studentId, 'schedules', scheduleId], 'removeCourse',  ...args) },
	reorderCourse(studentId, scheduleId, ...args)   { this._alter([studentId, 'schedules', scheduleId], 'reorderCourse', ...args) },
	reorderArea(studentId, areaId, ...args)         { this._alter([studentId, 'studies', areaId],       'reorder',       ...args) },
	/*eslint-enable no-multi-spaces, brace-style */
})

window.store = studentStore

export default studentStore
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2015 Google Inc. All Rights Reserved.
 *
 * 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.
 */

foam.CLASS({
  package: 'foam.u2',
  name: 'FloatView',
  extends: 'foam.u2.TextField',

  documentation: 'View for editing Float Properties.',

  css: `
    ^:read-only {
      border: none;
      background: rgba(0,0,0,0);
    }
  `,

  properties: [
    ['type', 'number'],
    { class: 'Float', name: 'data' },
    'precision',
    'min',
    'max',
    {
      class: 'Float',
      name: 'step',
      documentation: `The amount that the value should increment or decrement by
          when the arrow buttons in the input are clicked.`,
      value: 0.01
    }
  ],

  methods: [
    function initE() {
      this.SUPER();
      this.addClass(this.myClass());
      if ( this.min != undefined ) this.setAttribute('min', this.min);
      if ( this.max != undefined ) this.setAttribute('max', this.max);
      if ( this.step != undefined ) this.setAttribute('step', this.step);
    },

    function link() {
      this.attrSlot(null, this.onKey ? 'input' : null).relateFrom(
          this.data$,
          this.textToData.bind(this),
          this.dataToText.bind(this));
    },

    function fromProperty(p) {
      this.SUPER(p);

      this.precision = p.precision;
      this.min = p.min;
      this.max = p.max;
    },

    function formatNumber(val) {
      if ( ! val ) return '0';
      val = val.toFixed(this.precision);
      var i = val.length - 1;
      for ( ; i > 0 && val.charAt(i) === '0'; i -- ) {}
      return val.substring(0, val.charAt(i) === '.' ? i : i + 1);
    },

    function dataToText(val) {
      return this.precision !== undefined ?
          this.formatNumber(val) :
          '' + val;
    },

    function textToData(text) {
      return parseFloat(text) || 0;
    }
  ]
});
<commit_msg>Fix code style<commit_after>/**
 * @license
 * Copyright 2015 Google Inc. All Rights Reserved.
 *
 * 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.
 */

foam.CLASS({
  package: 'foam.u2',
  name: 'FloatView',
  extends: 'foam.u2.TextField',

  documentation: 'View for editing Float Properties.',

  css: `
    ^:read-only {
      border: none;
      background: rgba(0,0,0,0);
    }
  `,

  properties: [
    ['type', 'number'],
    { class: 'Float', name: 'data' },
    'precision',
    'min',
    'max',
    {
      class: 'Float',
      name: 'step',
      documentation: `The amount that the value should increment or decrement by
          when the arrow buttons in the input are clicked.`,
      value: 0.01
    }
  ],

  methods: [
    function initE() {
      this.SUPER();
      this.addClass(this.myClass());
      if ( this.min != undefined ) this.setAttribute('min', this.min);
      if ( this.max != undefined ) this.setAttribute('max', this.max);
      if ( this.step != undefined ) this.setAttribute('step', this.step);
    },

    function link() {
      this.attrSlot(null, this.onKey ? 'input' : null).relateFrom(
          this.data$,
          this.textToData.bind(this),
          this.dataToText.bind(this));
    },

    function fromProperty(p) {
      this.SUPER(p);

      this.precision = p.precision;
      this.min = p.min;
      this.max = p.max;
    },

    function formatNumber(val) {
      if ( ! val ) return '0';
      val = val.toFixed(this.precision);
      var i = val.length - 1;
      for ( ; i > 0 && val.charAt(i) === '0' ; i-- ) {}
      return val.substring(0, val.charAt(i) === '.' ? i : i + 1);
    },

    function dataToText(val) {
      return this.precision !== undefined ?
          this.formatNumber(val) :
          '' + val;
    },

    function textToData(text) {
      return parseFloat(text) || 0;
    }
  ]
});
<|endoftext|>


<commit_before>import {
  createAnchor,
  before,
  prepend,
  inDoc,
  mapNodeRange,
  removeNodeRange
} from '../util/index'

import {
  beforeWithTransition,
  removeWithTransition
} from '../transition/index'

/**
 * Abstraction for a partially-compiled fragment.
 * Can optionally compile content with a child scope.
 *
 * @param {Function} linker
 * @param {Vue} vm
 * @param {DocumentFragment} frag
 * @param {Vue} [host]
 * @param {Object} [scope]
 */

export default function Fragment (linker, vm, frag, host, scope, parentFrag) {
  this.children = []
  this.childFrags = []
  this.vm = vm
  this.scope = scope
  this.inserted = false
  this.parentFrag = parentFrag
  if (parentFrag) {
    parentFrag.childFrags.push(this)
  }
  this.unlink = linker(vm, frag, host, scope, this)
  var single = this.single =
    frag.childNodes.length === 1 &&
    // do not go single mode if the only node is an anchor
    !(frag.childNodes[0].__vue_anchor)
  if (single) {
    this.node = frag.childNodes[0]
    this.before = singleBefore
    this.remove = singleRemove
  } else {
    this.node = createAnchor('fragment-start')
    this.end = createAnchor('fragment-end')
    this.frag = frag
    prepend(this.node, frag)
    frag.appendChild(this.end)
    this.before = multiBefore
    this.remove = multiRemove
  }
  this.node.__vfrag__ = this
}

/**
 * Call attach/detach for all components contained within
 * this fragment. Also do so recursively for all child
 * fragments.
 *
 * @param {Function} hook
 */

Fragment.prototype.callHook = function (hook) {
  var i, l
  for (i = 0, l = this.childFrags.length; i < l; i++) {
    this.childFrags[i].callHook(hook)
  }
  for (i = 0, l = this.children.length; i < l; i++) {
    hook(this.children[i])
  }
}

/**
 * Insert fragment before target, single node version
 *
 * @param {Node} target
 * @param {Boolean} withTransition
 */

function singleBefore (target, withTransition) {
  this.inserted = true
  var method = withTransition !== false
    ? beforeWithTransition
    : before
  method(this.node, target, this.vm)
  if (inDoc(this.node)) {
    this.callHook(attach)
  }
}

/**
 * Remove fragment, single node version
 */

function singleRemove () {
  this.inserted = false
  var shouldCallRemove = inDoc(this.node)
  var self = this
  this.beforeRemove()
  removeWithTransition(this.node, this.vm, function () {
    if (shouldCallRemove) {
      self.callHook(detach)
    }
    self.destroy()
  })
}

/**
 * Insert fragment before target, multi-nodes version
 *
 * @param {Node} target
 * @param {Boolean} withTransition
 */

function multiBefore (target, withTransition) {
  this.inserted = true
  var vm = this.vm
  var method = withTransition !== false
    ? beforeWithTransition
    : before
  mapNodeRange(this.node, this.end, function (node) {
    method(node, target, vm)
  })
  if (inDoc(this.node)) {
    this.callHook(attach)
  }
}

/**
 * Remove fragment, multi-nodes version
 */

function multiRemove () {
  this.inserted = false
  var self = this
  var shouldCallRemove = inDoc(this.node)
  this.beforeRemove()
  removeNodeRange(this.node, this.end, this.vm, this.frag, function () {
    if (shouldCallRemove) {
      self.callHook(detach)
    }
    self.destroy()
  })
}

/**
 * Prepare the fragment for removal.
 */

Fragment.prototype.beforeRemove = function () {
  var i, l
  for (i = 0, l = this.childFrags.length; i < l; i++) {
    // call the same method recursively on child
    // fragments, depth-first
    this.childFrags[i].beforeRemove(false)
  }
  for (i = 0, l = this.children.length; i < l; i++) {
   // Call destroy for all contained instances,
   // with remove:false and defer:true.
   // Defer is necessary because we need to
   // keep the children to call detach hooks
   // on them.
    this.children[i].$destroy(false, true)
  }
  var dirs = this.unlink.dirs
  for (i = 0, l = dirs.length; i < l; i++) {
    // disable the watchers on all the directives
    // so that the rendered content stays the same
    // during removal.
    dirs[i]._watcher && dirs[i]._watcher.teardown()
  }
}

/**
 * Destroy the fragment.
 */

Fragment.prototype.destroy = function () {
  if (this.parentFrag) {
    this.parentFrag.childFrags.$remove(this)
  }
  this.unlink()
}

/**
 * Call attach hook for a Vue instance.
 *
 * @param {Vue} child
 */

function attach (child) {
  if (!child._isAttached) {
    child._callHook('attached')
  }
}

/**
 * Call detach hook for a Vue instance.
 *
 * @param {Vue} child
 */

function detach (child) {
  if (child._isAttached) {
    child._callHook('detached')
  }
}
<commit_msg>Fixes: __vfrag__ references prevent GC of Vue objects #2161<commit_after>import {
  createAnchor,
  before,
  prepend,
  inDoc,
  mapNodeRange,
  removeNodeRange
} from '../util/index'

import {
  beforeWithTransition,
  removeWithTransition
} from '../transition/index'

/**
 * Abstraction for a partially-compiled fragment.
 * Can optionally compile content with a child scope.
 *
 * @param {Function} linker
 * @param {Vue} vm
 * @param {DocumentFragment} frag
 * @param {Vue} [host]
 * @param {Object} [scope]
 */

export default function Fragment (linker, vm, frag, host, scope, parentFrag) {
  this.children = []
  this.childFrags = []
  this.vm = vm
  this.scope = scope
  this.inserted = false
  this.parentFrag = parentFrag
  if (parentFrag) {
    parentFrag.childFrags.push(this)
  }
  this.unlink = linker(vm, frag, host, scope, this)
  var single = this.single =
    frag.childNodes.length === 1 &&
    // do not go single mode if the only node is an anchor
    !(frag.childNodes[0].__vue_anchor)
  if (single) {
    this.node = frag.childNodes[0]
    this.before = singleBefore
    this.remove = singleRemove
  } else {
    this.node = createAnchor('fragment-start')
    this.end = createAnchor('fragment-end')
    this.frag = frag
    prepend(this.node, frag)
    frag.appendChild(this.end)
    this.before = multiBefore
    this.remove = multiRemove
  }
  this.node.__vfrag__ = this
}

/**
 * Call attach/detach for all components contained within
 * this fragment. Also do so recursively for all child
 * fragments.
 *
 * @param {Function} hook
 */

Fragment.prototype.callHook = function (hook) {
  var i, l
  for (i = 0, l = this.childFrags.length; i < l; i++) {
    this.childFrags[i].callHook(hook)
  }
  for (i = 0, l = this.children.length; i < l; i++) {
    hook(this.children[i])
  }
}

/**
 * Insert fragment before target, single node version
 *
 * @param {Node} target
 * @param {Boolean} withTransition
 */

function singleBefore (target, withTransition) {
  this.inserted = true
  var method = withTransition !== false
    ? beforeWithTransition
    : before
  method(this.node, target, this.vm)
  if (inDoc(this.node)) {
    this.callHook(attach)
  }
}

/**
 * Remove fragment, single node version
 */

function singleRemove () {
  this.inserted = false
  var shouldCallRemove = inDoc(this.node)
  var self = this
  this.beforeRemove()
  removeWithTransition(this.node, this.vm, function () {
    if (shouldCallRemove) {
      self.callHook(detach)
    }
    self.destroy()
  })
}

/**
 * Insert fragment before target, multi-nodes version
 *
 * @param {Node} target
 * @param {Boolean} withTransition
 */

function multiBefore (target, withTransition) {
  this.inserted = true
  var vm = this.vm
  var method = withTransition !== false
    ? beforeWithTransition
    : before
  mapNodeRange(this.node, this.end, function (node) {
    method(node, target, vm)
  })
  if (inDoc(this.node)) {
    this.callHook(attach)
  }
}

/**
 * Remove fragment, multi-nodes version
 */

function multiRemove () {
  this.inserted = false
  var self = this
  var shouldCallRemove = inDoc(this.node)
  this.beforeRemove()
  removeNodeRange(this.node, this.end, this.vm, this.frag, function () {
    if (shouldCallRemove) {
      self.callHook(detach)
    }
    self.destroy()
  })
}

/**
 * Prepare the fragment for removal.
 */

Fragment.prototype.beforeRemove = function () {
  var i, l
  for (i = 0, l = this.childFrags.length; i < l; i++) {
    // call the same method recursively on child
    // fragments, depth-first
    this.childFrags[i].beforeRemove(false)
  }
  for (i = 0, l = this.children.length; i < l; i++) {
   // Call destroy for all contained instances,
   // with remove:false and defer:true.
   // Defer is necessary because we need to
   // keep the children to call detach hooks
   // on them.
    this.children[i].$destroy(false, true)
  }
  var dirs = this.unlink.dirs
  for (i = 0, l = dirs.length; i < l; i++) {
    // disable the watchers on all the directives
    // so that the rendered content stays the same
    // during removal.
    dirs[i]._watcher && dirs[i]._watcher.teardown()
  }
}

/**
 * Destroy the fragment.
 */

Fragment.prototype.destroy = function () {
  if (this.parentFrag) {
    this.parentFrag.childFrags.$remove(this)
  }
  if (this.node) {
    this.node.__vfrag__ = null
  }
  this.unlink()
}

/**
 * Call attach hook for a Vue instance.
 *
 * @param {Vue} child
 */

function attach (child) {
  if (!child._isAttached) {
    child._callHook('attached')
  }
}

/**
 * Call detach hook for a Vue instance.
 *
 * @param {Vue} child
 */

function detach (child) {
  if (child._isAttached) {
    child._callHook('detached')
  }
}
<|endoftext|>


<commit_before>/**
 *  FullScreen widget:
 *
 *  var widget = new cdb.ui.common.FullScreen({
 *    doc: ".container", // optional; if not specified, we do the fullscreen of the whole window
 *    template: this.getTemplate("table/views/fullscreen")
 *  });
 *
 */

cdb.ui.common.FullScreen = cdb.core.View.extend({

  tagName: 'div',
  className: 'cartodb-fullscreen',

  events: {

    "click a": "_toggleFullScreen"

  },

  initialize: function() {

    _.bindAll(this, 'render');
    _.defaults(this.options, this.default_options);

    //this.model = new cdb.core.Model({
      //allowWheelOnFullscreen: false
    //});

    this._addWheelEvent();

  },

  _addWheelEvent: function() {

      var self    = this;
      var mapView = this.options.mapView;

      $(document).on('webkitfullscreenchange mozfullscreenchange fullscreenchange', function() {

        if ( !document.fullscreenElement && !document.webkitFullscreenElement && !document.mozFullScreenElement && !document.msFullscreenElement) {
          if (self.model.get("allowWheelOnFullscreen")) {
            mapView.options.map.set("scrollwheel", false);
          }
        }

        mapView.invalidateSize();

      });

  },

  _toggleFullScreen: function(ev) {

    ev.stopPropagation();
    ev.preventDefault();

    var doc   = window.document;
    var docEl = doc.documentElement;

    if (this.options.doc) { // we use a custom element
      docEl = $(this.options.doc)[0];
    }

    var requestFullScreen = docEl.requestFullscreen || docEl.mozRequestFullScreen || docEl.webkitRequestFullScreen || docEl.msRequestFullscreen;
    var cancelFullScreen = doc.exitFullscreen || doc.mozCancelFullScreen || doc.webkitExitFullscreen || doc.msExitFullscreen;

    var mapView = this.options.mapView;

    if (!doc.fullscreenElement && !doc.mozFullScreenElement && !doc.webkitFullscreenElement && !doc.msFullscreenElement) {

      if (docEl.webkitRequestFullScreen) {
        // Cartodb.js #361 :: Full screen button not working on Safari 8.0.3 #361
        // Safari has a bug that fullScreen doestn't work with Element.ALLOW_KEYBOARD_INPUT);
        // Reference: Ehttp://stackoverflow.com/questions/8427413/webkitrequestfullscreen-fails-when-passing-element-allow-keyboard-input-in-safar
        requestFullScreen.call(docEl, undefined);
      } else {
        // CartoDB.js #412 :: Fullscreen button is throwing errors
        // Nowadays (2015/03/25), fullscreen is not supported in iOS Safari. Reference: http://caniuse.com/#feat=fullscreen
        if (requestFullScreen !== undefined) {
          requestFullScreen.call(docEl);
        }
      }

      if (mapView) {

        if (this.model.get("allowWheelOnFullscreen")) {
          mapView.options.map.set("scrollwheel", true);
        }

      }

    } else {

      cancelFullScreen.call(doc);

    }
  },

  render: function() {

    var $el = this.$el;

    var options = _.extend(this.options);

    $el.html(this.options.template(options));

    return this;
  }

});
<commit_msg>Fix with @pablo's comment<commit_after>/**
 *  FullScreen widget:
 *
 *  var widget = new cdb.ui.common.FullScreen({
 *    doc: ".container", // optional; if not specified, we do the fullscreen of the whole window
 *    template: this.getTemplate("table/views/fullscreen")
 *  });
 *
 */

cdb.ui.common.FullScreen = cdb.core.View.extend({

  tagName: 'div',
  className: 'cartodb-fullscreen',

  events: {

    "click a": "_toggleFullScreen"

  },

  initialize: function() {

    _.bindAll(this, 'render');
    _.defaults(this.options, this.default_options);

    //this.model = new cdb.core.Model({
      //allowWheelOnFullscreen: false
    //});

    this._addWheelEvent();

  },

  _addWheelEvent: function() {

      var self    = this;
      var mapView = this.options.mapView;

      $(document).on('webkitfullscreenchange mozfullscreenchange fullscreenchange', function() {

        if ( !document.fullscreenElement && !document.webkitFullscreenElement && !document.mozFullScreenElement && !document.msFullscreenElement) {
          if (self.model.get("allowWheelOnFullscreen")) {
            mapView.options.map.set("scrollwheel", false);
          }
        }

        mapView.invalidateSize();

      });

  },

  _toggleFullScreen: function(ev) {

    ev.stopPropagation();
    ev.preventDefault();

    var doc   = window.document;
    var docEl = doc.documentElement;

    if (this.options.doc) { // we use a custom element
      docEl = $(this.options.doc)[0];
    }

    var requestFullScreen = docEl.requestFullscreen || docEl.mozRequestFullScreen || docEl.webkitRequestFullScreen || docEl.msRequestFullscreen;
    var cancelFullScreen = doc.exitFullscreen || doc.mozCancelFullScreen || doc.webkitExitFullscreen || doc.msExitFullscreen;

    var mapView = this.options.mapView;

    if (!doc.fullscreenElement && !doc.mozFullScreenElement && !doc.webkitFullscreenElement && !doc.msFullscreenElement) {

      if (docEl.webkitRequestFullScreen) {
        // Cartodb.js #361 :: Full screen button not working on Safari 8.0.3 #361
        // Safari has a bug that fullScreen doestn't work with Element.ALLOW_KEYBOARD_INPUT);
        // Reference: Ehttp://stackoverflow.com/questions/8427413/webkitrequestfullscreen-fails-when-passing-element-allow-keyboard-input-in-safar
        requestFullScreen.call(docEl, undefined);
      } else {
        // CartoDB.js #412 :: Fullscreen button is throwing errors
        // Nowadays (2015/03/25), fullscreen is not supported in iOS Safari. Reference: http://caniuse.com/#feat=fullscreen
        if (requestFullScreen) {
          requestFullScreen.call(docEl);
        }
      }

      if (mapView) {

        if (this.model.get("allowWheelOnFullscreen")) {
          mapView.options.map.set("scrollwheel", true);
        }

      }

    } else {

      cancelFullScreen.call(doc);

    }
  },

  render: function() {

    var $el = this.$el;

    var options = _.extend(this.options);

    $el.html(this.options.template(options));

    return this;
  }

});
<|endoftext|>


<commit_before>/*
 * data.js
 *
 * https://github.com/istvan-antal/data.js
 *
 * Licensed under the MIT license.
 */

Data.geolocation.Point = function (latitude, longitude) {
    if (typeof latitude !== 'number' || isNaN(latitude)) {
        throw new Error('latitude is not a number.');
    }

    if (typeof longitude !== 'number' || isNaN(longitude)) {
        throw new Error('longitude is not a number.');
    }

    this._latitude = latitude;
    this._longitude = longitude;
};

Data.geolocation.Point.prototype.getLatitude = function () {
    return this._latitude;
};

Data.geolocation.Point.prototype.getLongitude = function () {
    return this._longitude;
};

/**
 * Returns the distance from another point in kilometers.
 *
 * @param {Data.geolocation.Point} point
 * @returns {Number}
 */
Data.geolocation.Point.prototype.getDistanceFrom = function (point) {
    var acos = Math.acos,
        sin = Math.sin,
        cos = Math.cos,
        aLat = Data.geometry.Angle.getRadiansFromDegrees(this.getLatitude()),
        aLng = Data.geometry.Angle.getRadiansFromDegrees(this.getLongitude()),
        bLat = Data.geometry.Angle.getRadiansFromDegrees(point.getLatitude()),
        bLng = Data.geometry.Angle.getRadiansFromDegrees(point.getLongitude());

    if (!(point instanceof Data.geolocation.Point)) {
        throw new Error('point is of invalid type');
    }

    return acos(
            sin(aLat) * sin(bLat) +
            cos(aLat) * cos(bLat) * cos(bLng - aLng)) *
    6371;
};<commit_msg>Added some comments<commit_after>/*
 * data.js
 *
 * https://github.com/istvan-antal/data.js
 *
 * Licensed under the MIT license.
 */

Data.geolocation.Point = function (latitude, longitude) {
    if (typeof latitude !== 'number' || isNaN(latitude)) {
        throw new Error('latitude is not a number.');
    }

    if (typeof longitude !== 'number' || isNaN(longitude)) {
        throw new Error('longitude is not a number.');
    }

    this._latitude = latitude;
    this._longitude = longitude;
};

/**
 * @returns {number}
 */
Data.geolocation.Point.prototype.getLatitude = function () {
    return this._latitude;
};

/**
 * @returns {number}
 */
Data.geolocation.Point.prototype.getLongitude = function () {
    return this._longitude;
};

/**
 * Returns the distance from another point in kilometers.
 *
 * @param {Data.geolocation.Point} point
 * @returns {number}
 */
Data.geolocation.Point.prototype.getDistanceFrom = function (point) {
    var acos = Math.acos,
        sin = Math.sin,
        cos = Math.cos,
        aLat = Data.geometry.Angle.getRadiansFromDegrees(this.getLatitude()),
        aLng = Data.geometry.Angle.getRadiansFromDegrees(this.getLongitude()),
        bLat = Data.geometry.Angle.getRadiansFromDegrees(point.getLatitude()),
        bLng = Data.geometry.Angle.getRadiansFromDegrees(point.getLongitude());

    if (!(point instanceof Data.geolocation.Point)) {
        throw new Error('point is of invalid type');
    }

    return acos(
            sin(aLat) * sin(bLat) +
            cos(aLat) * cos(bLat) * cos(bLng - aLng)) *
    6371;
};<|endoftext|>


<commit_before>/**
 * @fileOverview 自定义的 shape
 * @author dxq613@gmail.com
 * @author sima.zhang1990@gmail.com
 */

const Util = require('../../util');
const Shape = require('./shape');
const ShapeUtil = require('../util/shape');
const Global = require('../../global');

function _parseValue(value) {
  if (!Util.isArray(value)) {
    value = [ value ];
  }
  const min = value[0]; // 最小值
  const max = value[value.length - 1]; // 最大值
  const min1 = value.length > 1 ? value[1] : min;
  const max1 = value.length > 3 ? value[3] : max;
  const median = value.length > 2 ? value[2] : min1;

  return {
    min, // 最小值
    max, // 最大值
    min1,
    max1,
    median
  };
}

function addPoints(from, to) {
  Util.each(from, function(subArr) {
    to.push({
      x: subArr[0],
      y: subArr[1]
    });
  });
}

function getAttrs(cfg) {
  const defaultAttrs = Global.shape.schema;
  const lineAttrs = Util.mix({}, defaultAttrs, cfg.style);
  ShapeUtil.addStrokeAttrs(lineAttrs, cfg);
  return lineAttrs;
}

function getFillAttrs(cfg) {
  const defaultAttrs = Global.shape.schema;
  const lineAttrs = Util.mix({}, defaultAttrs, cfg.style);
  ShapeUtil.addFillAttrs(lineAttrs, cfg);
  if (cfg.color) {
    lineAttrs.stroke = lineAttrs.stroke || cfg.color;
  }
  return lineAttrs;
}

function getBoxPoints(x, y, width) {
  const points = [];
  let pointsArray;
  let obj;
  if (Util.isArray(y)) { // 2维
    obj = _parseValue(y);
    pointsArray = [
      [ x - width / 2, obj.max ],
      [ x + width / 2, obj.max ],
      [ x, obj.max ],
      [ x, obj.max1 ],
      [ x - width / 2, obj.min1 ],
      [ x - width / 2, obj.max1 ],
      [ x + width / 2, obj.max1 ],
      [ x + width / 2, obj.min1 ],
      [ x, obj.min1 ],
      [ x, obj.min ],
      [ x - width / 2, obj.min ],
      [ x + width / 2, obj.min ],
      [ x - width / 2, obj.median ],
      [ x + width / 2, obj.median ]
    ];
  } else { // 只有一个维度
    y = y || 0.5;
    obj = _parseValue(x);
    pointsArray = [
      [ obj.min, y - width / 2 ],
      [ obj.min, y + width / 2 ],
      [ obj.min, y ],
      [ obj.min1, y ],
      [ obj.min1, y - width / 2 ],
      [ obj.min1, y + width / 2 ],
      [ obj.max1, y + width / 2 ],
      [ obj.max1, y - width / 2 ],
      [ obj.max1, y ],
      [ obj.max, y ],
      [ obj.max, y - width / 2 ],
      [ obj.max, y + width / 2 ],
      [ obj.median, y - width / 2 ],
      [ obj.median, y + width / 2 ]
    ];
  }
  addPoints(pointsArray, points);
  return points;
}

function _sortValue(value) {
  if (!Util.isArray(value)) {
    value = [ value ];
  }
  // 从大到小排序
  const sorted = value.sort(function(a, b) {
    return a < b ? 1 : -1;
  });

  const length = sorted.length;
  if (length < 4) {
    const min = sorted[length - 1];
    for (let i = 0; i < (4 - length); i++) {
      sorted.push(min);
    }
  }

  return sorted;
}

// 获取K线图的points
function getCandlePoints(x, y, width) {
  const yValues = _sortValue(y);
  const points = [{
    x,
    y: yValues[0]
  }, {
    x,
    y: yValues[1]
  }, {
    x: x - width / 2,
    y: yValues[2]
  }, {
    x: x - width / 2,
    y: yValues[1]
  }, {
    x: x + width / 2,
    y: yValues[1]
  }, {
    x: x + width / 2,
    y: yValues[2]
  }, {
    x,
    y: yValues[2]
  }, {
    x,
    y: yValues[3]
  }]; // 按照顺时针连接
  return points;
}

function getBoxPath(points) {
  const path = [
    [ 'M', points[0].x, points[0].y ],
    [ 'L', points[1].x, points[1].y ],
    [ 'M', points[2].x, points[2].y ],
    [ 'L', points[3].x, points[3].y ],
    [ 'M', points[4].x, points[4].y ],
    [ 'L', points[5].x, points[5].y ],
    [ 'L', points[6].x, points[6].y ],
    [ 'L', points[7].x, points[7].y ],
    [ 'L', points[4].x, points[4].y ], // 封闭 z
    [ 'Z' ],
    [ 'M', points[8].x, points[8].y ],
    [ 'L', points[9].x, points[9].y ],
    [ 'M', points[10].x, points[10].y ],
    [ 'L', points[11].x, points[11].y ],
    [ 'M', points[12].x, points[12].y ],
    [ 'L', points[13].x, points[13].y ]
  ];
  return path;
}

function getCandlePath(points) {
  const path = [
    [ 'M', points[0].x, points[0].y ],
    [ 'L', points[1].x, points[1].y ],
    [ 'M', points[2].x, points[2].y ],
    [ 'L', points[3].x, points[3].y ],
    [ 'L', points[4].x, points[4].y ],
    [ 'L', points[5].x, points[5].y ],
    [ 'Z' ],
    [ 'M', points[6].x, points[6].y ],
    [ 'L', points[7].x, points[7].y ]
  ];
  return path;
}

const Schema = Shape.registerFactory('schema', {
  defaultShapeType: '',
  getActiveCfg(type, cfg) {
    if (type === 'box') {
      const lineWidth = cfg.lineWidth || 1;
      return {
        lineWidth: lineWidth + 1
      };
    }
    const opacity = cfg.fillOpacity || cfg.opacity || 1;
    return {
      fillOpacity: opacity - 0.15,
      strokeOpacity: opacity - 0.15
    };
  },
  getSelectedCfg(type, cfg) {
    if (cfg && cfg.style) {
      return cfg.style;
    }
    return this.getActiveCfg(type, cfg);
  }
});

// 箱线图
Shape.registerShape('schema', 'box', {
  getPoints(pointInfo) {
    return getBoxPoints(pointInfo.x, pointInfo.y, pointInfo.size);
  },
  draw(cfg, container) {
    const attrs = getAttrs(cfg);
    let path = getBoxPath(cfg.points);
    path = this.parsePath(path);
    return container.addShape('path', {
      attrs: Util.mix(attrs, {
        path
      })
    });
  },
  getMarkerCfg(cfg) {
    return {
      symbol(x, y, r) {
        const yValues = [ y - 6, y - 3, y, y + 3, y + 6 ];
        const points = getBoxPoints(x, yValues, r);
        return [
          [ 'M', points[0].x + 1, points[0].y ],
          [ 'L', points[1].x - 1, points[1].y ],
          [ 'M', points[2].x, points[2].y ],
          [ 'L', points[3].x, points[3].y ],
          [ 'M', points[4].x, points[4].y ],
          [ 'L', points[5].x, points[5].y ],
          [ 'L', points[6].x, points[6].y ],
          [ 'L', points[7].x, points[7].y ],
          [ 'L', points[4].x, points[4].y ],
          [ 'Z' ],
          [ 'M', points[8].x, points[8].y ],
          [ 'L', points[9].x, points[9].y ],
          [ 'M', points[10].x + 1, points[10].y ],
          [ 'L', points[11].x - 1, points[11].y ],
          [ 'M', points[12].x, points[12].y ],
          [ 'L', points[13].x, points[13].y ]
        ];
      },
      radius: 6,
      lineWidth: 1,
      stroke: cfg.color
    };
  }
});

// K线
Shape.registerShape('schema', 'candle', {
  getPoints(pointInfo) {
    return getCandlePoints(pointInfo.x, pointInfo.y, pointInfo.size);
  },
  draw(cfg, container) {
    const attrs = getFillAttrs(cfg);
    let path = getCandlePath(cfg.points);
    path = this.parsePath(path);
    return container.addShape('path', {
      attrs: Util.mix(attrs, {
        path
      })
    });
  },
  getMarkerCfg(cfg) {
    return {
      symbol(x, y, r) {
        y = [ y + 7.5, y + 3, y - 3, y - 7.5 ];
        const points = getCandlePoints(x, y, r);
        return [
          [ 'M', points[0].x, points[0].y ],
          [ 'L', points[1].x, points[1].y ],
          [ 'M', points[2].x, points[2].y ],
          [ 'L', points[3].x, points[3].y ],
          [ 'L', points[4].x, points[4].y ],
          [ 'L', points[5].x, points[5].y ],
          [ 'Z' ],
          [ 'M', points[6].x, points[6].y ],
          [ 'L', points[7].x, points[7].y ]
        ];
      },
      lineWidth: 1,
      stroke: cfg.color,
      fill: cfg.color,
      radius: 6
    };
  }
});

module.exports = Schema;
<commit_msg>Use cfg.color for lineAttrs.stroke if available.<commit_after>/**
 * @fileOverview 自定义的 shape
 * @author dxq613@gmail.com
 * @author sima.zhang1990@gmail.com
 */

const Util = require('../../util');
const Shape = require('./shape');
const ShapeUtil = require('../util/shape');
const Global = require('../../global');

function _parseValue(value) {
  if (!Util.isArray(value)) {
    value = [ value ];
  }
  const min = value[0]; // 最小值
  const max = value[value.length - 1]; // 最大值
  const min1 = value.length > 1 ? value[1] : min;
  const max1 = value.length > 3 ? value[3] : max;
  const median = value.length > 2 ? value[2] : min1;

  return {
    min, // 最小值
    max, // 最大值
    min1,
    max1,
    median
  };
}

function addPoints(from, to) {
  Util.each(from, function(subArr) {
    to.push({
      x: subArr[0],
      y: subArr[1]
    });
  });
}

function getAttrs(cfg) {
  const defaultAttrs = Global.shape.schema;
  const lineAttrs = Util.mix({}, defaultAttrs, cfg.style);
  ShapeUtil.addStrokeAttrs(lineAttrs, cfg);
  return lineAttrs;
}

function getFillAttrs(cfg) {
  const defaultAttrs = Global.shape.schema;
  const lineAttrs = Util.mix({}, defaultAttrs, cfg.style);
  ShapeUtil.addFillAttrs(lineAttrs, cfg);
  if (cfg.color) {
    lineAttrs.stroke = cfg.color || lineAttrs.stroke;
  }
  return lineAttrs;
}

function getBoxPoints(x, y, width) {
  const points = [];
  let pointsArray;
  let obj;
  if (Util.isArray(y)) { // 2维
    obj = _parseValue(y);
    pointsArray = [
      [ x - width / 2, obj.max ],
      [ x + width / 2, obj.max ],
      [ x, obj.max ],
      [ x, obj.max1 ],
      [ x - width / 2, obj.min1 ],
      [ x - width / 2, obj.max1 ],
      [ x + width / 2, obj.max1 ],
      [ x + width / 2, obj.min1 ],
      [ x, obj.min1 ],
      [ x, obj.min ],
      [ x - width / 2, obj.min ],
      [ x + width / 2, obj.min ],
      [ x - width / 2, obj.median ],
      [ x + width / 2, obj.median ]
    ];
  } else { // 只有一个维度
    y = y || 0.5;
    obj = _parseValue(x);
    pointsArray = [
      [ obj.min, y - width / 2 ],
      [ obj.min, y + width / 2 ],
      [ obj.min, y ],
      [ obj.min1, y ],
      [ obj.min1, y - width / 2 ],
      [ obj.min1, y + width / 2 ],
      [ obj.max1, y + width / 2 ],
      [ obj.max1, y - width / 2 ],
      [ obj.max1, y ],
      [ obj.max, y ],
      [ obj.max, y - width / 2 ],
      [ obj.max, y + width / 2 ],
      [ obj.median, y - width / 2 ],
      [ obj.median, y + width / 2 ]
    ];
  }
  addPoints(pointsArray, points);
  return points;
}

function _sortValue(value) {
  if (!Util.isArray(value)) {
    value = [ value ];
  }
  // 从大到小排序
  const sorted = value.sort(function(a, b) {
    return a < b ? 1 : -1;
  });

  const length = sorted.length;
  if (length < 4) {
    const min = sorted[length - 1];
    for (let i = 0; i < (4 - length); i++) {
      sorted.push(min);
    }
  }

  return sorted;
}

// 获取K线图的points
function getCandlePoints(x, y, width) {
  const yValues = _sortValue(y);
  const points = [{
    x,
    y: yValues[0]
  }, {
    x,
    y: yValues[1]
  }, {
    x: x - width / 2,
    y: yValues[2]
  }, {
    x: x - width / 2,
    y: yValues[1]
  }, {
    x: x + width / 2,
    y: yValues[1]
  }, {
    x: x + width / 2,
    y: yValues[2]
  }, {
    x,
    y: yValues[2]
  }, {
    x,
    y: yValues[3]
  }]; // 按照顺时针连接
  return points;
}

function getBoxPath(points) {
  const path = [
    [ 'M', points[0].x, points[0].y ],
    [ 'L', points[1].x, points[1].y ],
    [ 'M', points[2].x, points[2].y ],
    [ 'L', points[3].x, points[3].y ],
    [ 'M', points[4].x, points[4].y ],
    [ 'L', points[5].x, points[5].y ],
    [ 'L', points[6].x, points[6].y ],
    [ 'L', points[7].x, points[7].y ],
    [ 'L', points[4].x, points[4].y ], // 封闭 z
    [ 'Z' ],
    [ 'M', points[8].x, points[8].y ],
    [ 'L', points[9].x, points[9].y ],
    [ 'M', points[10].x, points[10].y ],
    [ 'L', points[11].x, points[11].y ],
    [ 'M', points[12].x, points[12].y ],
    [ 'L', points[13].x, points[13].y ]
  ];
  return path;
}

function getCandlePath(points) {
  const path = [
    [ 'M', points[0].x, points[0].y ],
    [ 'L', points[1].x, points[1].y ],
    [ 'M', points[2].x, points[2].y ],
    [ 'L', points[3].x, points[3].y ],
    [ 'L', points[4].x, points[4].y ],
    [ 'L', points[5].x, points[5].y ],
    [ 'Z' ],
    [ 'M', points[6].x, points[6].y ],
    [ 'L', points[7].x, points[7].y ]
  ];
  return path;
}

const Schema = Shape.registerFactory('schema', {
  defaultShapeType: '',
  getActiveCfg(type, cfg) {
    if (type === 'box') {
      const lineWidth = cfg.lineWidth || 1;
      return {
        lineWidth: lineWidth + 1
      };
    }
    const opacity = cfg.fillOpacity || cfg.opacity || 1;
    return {
      fillOpacity: opacity - 0.15,
      strokeOpacity: opacity - 0.15
    };
  },
  getSelectedCfg(type, cfg) {
    if (cfg && cfg.style) {
      return cfg.style;
    }
    return this.getActiveCfg(type, cfg);
  }
});

// 箱线图
Shape.registerShape('schema', 'box', {
  getPoints(pointInfo) {
    return getBoxPoints(pointInfo.x, pointInfo.y, pointInfo.size);
  },
  draw(cfg, container) {
    const attrs = getAttrs(cfg);
    let path = getBoxPath(cfg.points);
    path = this.parsePath(path);
    return container.addShape('path', {
      attrs: Util.mix(attrs, {
        path
      })
    });
  },
  getMarkerCfg(cfg) {
    return {
      symbol(x, y, r) {
        const yValues = [ y - 6, y - 3, y, y + 3, y + 6 ];
        const points = getBoxPoints(x, yValues, r);
        return [
          [ 'M', points[0].x + 1, points[0].y ],
          [ 'L', points[1].x - 1, points[1].y ],
          [ 'M', points[2].x, points[2].y ],
          [ 'L', points[3].x, points[3].y ],
          [ 'M', points[4].x, points[4].y ],
          [ 'L', points[5].x, points[5].y ],
          [ 'L', points[6].x, points[6].y ],
          [ 'L', points[7].x, points[7].y ],
          [ 'L', points[4].x, points[4].y ],
          [ 'Z' ],
          [ 'M', points[8].x, points[8].y ],
          [ 'L', points[9].x, points[9].y ],
          [ 'M', points[10].x + 1, points[10].y ],
          [ 'L', points[11].x - 1, points[11].y ],
          [ 'M', points[12].x, points[12].y ],
          [ 'L', points[13].x, points[13].y ]
        ];
      },
      radius: 6,
      lineWidth: 1,
      stroke: cfg.color
    };
  }
});

// K线
Shape.registerShape('schema', 'candle', {
  getPoints(pointInfo) {
    return getCandlePoints(pointInfo.x, pointInfo.y, pointInfo.size);
  },
  draw(cfg, container) {
    const attrs = getFillAttrs(cfg);
    let path = getCandlePath(cfg.points);
    path = this.parsePath(path);
    return container.addShape('path', {
      attrs: Util.mix(attrs, {
        path
      })
    });
  },
  getMarkerCfg(cfg) {
    return {
      symbol(x, y, r) {
        y = [ y + 7.5, y + 3, y - 3, y - 7.5 ];
        const points = getCandlePoints(x, y, r);
        return [
          [ 'M', points[0].x, points[0].y ],
          [ 'L', points[1].x, points[1].y ],
          [ 'M', points[2].x, points[2].y ],
          [ 'L', points[3].x, points[3].y ],
          [ 'L', points[4].x, points[4].y ],
          [ 'L', points[5].x, points[5].y ],
          [ 'Z' ],
          [ 'M', points[6].x, points[6].y ],
          [ 'L', points[7].x, points[7].y ]
        ];
      },
      lineWidth: 1,
      stroke: cfg.color,
      fill: cfg.color,
      radius: 6
    };
  }
});

module.exports = Schema;
<|endoftext|>


<commit_before>import db from './db'
import {enhance as enhanceHanson} from '../area-tools'
import some from 'lodash/some'
import maxBy from 'lodash/maxBy'
import isArray from 'lodash/isArray'
import yaml from 'js-yaml'

function resolveArea(areas, query) {
	if (!('revision' in query)) {
		return maxBy(areas, 'revision')
	}
	else if (some(areas, possibility => 'dateAdded' in possibility)) {
		return maxBy(areas, 'dateAdded')
	}
	else {
		return maxBy(areas, possibility => possibility.sourcePath.length)
	}
}

function loadArea(areaQuery) {
	const {name, type, revision, source, isCustom} = areaQuery

	let area = {...areaQuery}
	if (isCustom && source) {
		return {...areaQuery, _area: enhanceHanson(yaml.safeLoad(source))}
	}

	let dbQuery = {name: [name], type: [type]}
	if (revision && revision !== 'latest') {
		dbQuery.revision = [revision]
	}

	return db.store('areas').query(dbQuery)
		.then(result => {
			if (result === undefined) {
				return {...areaQuery, _error: `the area "${name}" (${type}) could not be found with the query ${JSON.stringify(dbQuery)}`}
			}

			if (result.length === 1) {
				result = result[0]
			}
			else if (result.length >= 2) {
				result = resolveArea(result, dbQuery)
			}
			else {
				result = {name, type, revision, _error: `the area "${name}" (${type}) could not be found with the query ${JSON.stringify(dbQuery)}`}
			}

			return {...areaQuery, _area: enhanceHanson(result)}
		})
		.catch(err => {
			console.error(err)  // we can probably remove this in the future
			area._error = `Could not find area ${JSON.stringify(dbQuery)} (error: ${err.message})`
			return area
		})
}

const promiseCache = new Map()

export default async function getArea({name, type, revision, source, isCustom}, {cache=[]}) {
	let cachedArea = find(cache, a => (a.name === name) && (a.type === type) && (revision === 'latest' ? true : a.revision === revision))
	if (cachedArea) {
		console.log('loadArea used cached area')
		return cachedArea
	}

	let id = `{${name}, ${type}, ${revision}}`
	if (promiseCache.has(id)) {
		return promiseCache.get(id)
	}

	let promise = loadArea({name, type, revision, source, isCustom})

	promiseCache.set(id, promise)

	let area = await promiseCache.get(id)

	promiseCache.delete(id)

	return area
}
<commit_msg>make loadArea return a promise from either exit point<commit_after>import db from './db'
import {enhance as enhanceHanson} from '../area-tools'
import some from 'lodash/some'
import maxBy from 'lodash/maxBy'
import isArray from 'lodash/isArray'
import yaml from 'js-yaml'

function resolveArea(areas, query) {
	if (!('revision' in query)) {
		return maxBy(areas, 'revision')
	}
	else if (some(areas, possibility => 'dateAdded' in possibility)) {
		return maxBy(areas, 'dateAdded')
	}
	else {
		return maxBy(areas, possibility => possibility.sourcePath.length)
	}
}

function loadArea(areaQuery) {
	const {name, type, revision, source, isCustom} = areaQuery

	let area = {...areaQuery}
	if (isCustom && source) {
		return Promise.resolve({...areaQuery, _area: enhanceHanson(yaml.safeLoad(source))})
	}

	let dbQuery = {name: [name], type: [type]}
	if (revision && revision !== 'latest') {
		dbQuery.revision = [revision]
	}

	return db.store('areas').query(dbQuery)
		.then(result => {
			if (result === undefined) {
				return {...areaQuery, _error: `the area "${name}" (${type}) could not be found with the query ${JSON.stringify(dbQuery)}`}
			}

			if (result.length === 1) {
				result = result[0]
			}
			else if (result.length >= 2) {
				result = resolveArea(result, dbQuery)
			}
			else {
				result = {name, type, revision, _error: `the area "${name}" (${type}) could not be found with the query ${JSON.stringify(dbQuery)}`}
			}

			return {...areaQuery, _area: enhanceHanson(result)}
		})
		.catch(err => {
			console.error(err)  // we can probably remove this in the future
			area._error = `Could not find area ${JSON.stringify(dbQuery)} (error: ${err.message})`
			return area
		})
}

const promiseCache = new Map()

export default async function getArea({name, type, revision, source, isCustom}, {cache=[]}) {
	let cachedArea = find(cache, a => (a.name === name) && (a.type === type) && (revision === 'latest' ? true : a.revision === revision))
	if (cachedArea) {
		console.log('loadArea used cached area')
		return cachedArea
	}

	let id = `{${name}, ${type}, ${revision}}`
	if (promiseCache.has(id)) {
		return promiseCache.get(id)
	}

	let promise = loadArea({name, type, revision, source, isCustom})

	promiseCache.set(id, promise)

	let area = await promiseCache.get(id)

	promiseCache.delete(id)

	return area
}
<|endoftext|>


<commit_before>import {
  useReducer,
  useRef,
  useEffect,
  useMemo,
  useLayoutEffect,
  useContext
} from 'react'
import invariant from 'invariant'
import { useReduxContext as useDefaultReduxContext } from './useReduxContext'
import Subscription from '../utils/Subscription'
import { ReactReduxContext } from '../components/Context'

// React currently throws a warning when using useLayoutEffect on the server.
// To get around it, we can conditionally useEffect on the server (no-op) and
// useLayoutEffect in the browser. We need useLayoutEffect to ensure the store
// subscription callback always has the selector from the latest render commit
// available, otherwise a store update may happen between render and the effect,
// which may cause missed updates; we also must ensure the store subscription
// is created synchronously, otherwise a store update may occur before the
// subscription is created and an inconsistent state may be observed
const useIsomorphicLayoutEffect =
  typeof window !== 'undefined' &&
  typeof window.document !== 'undefined' &&
  typeof window.document.createElement !== 'undefined'
    ? useLayoutEffect
    : useEffect

const refEquality = (a, b) => a === b

function useSelectorWithStoreAndSubscription(
  selector,
  equalityFn,
  store,
  contextSub
) {
  const [, forceRender] = useReducer(s => s + 1, 0)

  const subscription = useMemo(() => new Subscription(store, contextSub), [
    store,
    contextSub
  ])

  const latestSubscriptionCallbackError = useRef()
  const latestSelector = useRef()
  const latestSelectedState = useRef()

  let selectedState

  try {
    if (
      selector !== latestSelector.current ||
      latestSubscriptionCallbackError.current
    ) {
      selectedState = selector(store.getState())
    } else {
      selectedState = latestSelectedState.current
    }
  } catch (err) {
    let errorMessage = `An error occured while selecting the store state: ${err.message}.`

    if (latestSubscriptionCallbackError.current) {
      errorMessage += `\nThe error may be correlated with this previous error:\n${latestSubscriptionCallbackError.current.stack}\n\nOriginal stack trace:`
    }

    throw new Error(errorMessage)
  }

  useIsomorphicLayoutEffect(() => {
    latestSelector.current = selector
    latestSelectedState.current = selectedState
    latestSubscriptionCallbackError.current = undefined
  })

  useIsomorphicLayoutEffect(() => {
    function checkForUpdates() {
      try {
        const newSelectedState = latestSelector.current(store.getState())

        if (equalityFn(newSelectedState, latestSelectedState.current)) {
          return
        }

        latestSelectedState.current = newSelectedState
      } catch (err) {
        // we ignore all errors here, since when the component
        // is re-rendered, the selectors are called again, and
        // will throw again, if neither props nor store state
        // changed
        latestSubscriptionCallbackError.current = err
      }

      forceRender({})
    }

    subscription.onStateChange = checkForUpdates
    subscription.trySubscribe()

    checkForUpdates()

    return () => subscription.tryUnsubscribe()
  }, [store, subscription])

  return selectedState
}

/**
 * Hook factory, which creates a `useSelector` hook bound to a given context.
 *
 * @param {Function} [context=ReactReduxContext] Context passed to your `<Provider>`.
 * @returns {Function} A `useSelector` hook bound to the specified context.
 */
export function createSelectorHook(context = ReactReduxContext) {
  const useReduxContext =
    context === ReactReduxContext
      ? useDefaultReduxContext
      : () => useContext(context)
  return function useSelector(selector, equalityFn = refEquality) {
    invariant(selector, `You must pass a selector to useSelectors`)

    const { store, subscription: contextSub } = useReduxContext()

    return useSelectorWithStoreAndSubscription(
      selector,
      equalityFn,
      store,
      contextSub
    )
  }
}

/**
 * A hook to access the redux store's state. This hook takes a selector function
 * as an argument. The selector is called with the store state.
 *
 * This hook takes an optional equality comparison function as the second parameter
 * that allows you to customize the way the selected state is compared to determine
 * whether the component needs to be re-rendered.
 *
 * @param {Function} selector the selector function
 * @param {Function=} equalityFn the function that will be used to determine equality
 *
 * @returns {any} the selected state
 *
 * @example
 *
 * import React from 'react'
 * import { useSelector } from 'react-redux'
 *
 * export const CounterComponent = () => {
 *   const counter = useSelector(state => state.counter)
 *   return
{counter}

 * }
 */
export const useSelector = createSelectorHook()
<commit_msg>fix spelling in error message (#1427)<commit_after>import {
  useReducer,
  useRef,
  useEffect,
  useMemo,
  useLayoutEffect,
  useContext
} from 'react'
import invariant from 'invariant'
import { useReduxContext as useDefaultReduxContext } from './useReduxContext'
import Subscription from '../utils/Subscription'
import { ReactReduxContext } from '../components/Context'

// React currently throws a warning when using useLayoutEffect on the server.
// To get around it, we can conditionally useEffect on the server (no-op) and
// useLayoutEffect in the browser. We need useLayoutEffect to ensure the store
// subscription callback always has the selector from the latest render commit
// available, otherwise a store update may happen between render and the effect,
// which may cause missed updates; we also must ensure the store subscription
// is created synchronously, otherwise a store update may occur before the
// subscription is created and an inconsistent state may be observed
const useIsomorphicLayoutEffect =
  typeof window !== 'undefined' &&
  typeof window.document !== 'undefined' &&
  typeof window.document.createElement !== 'undefined'
    ? useLayoutEffect
    : useEffect

const refEquality = (a, b) => a === b

function useSelectorWithStoreAndSubscription(
  selector,
  equalityFn,
  store,
  contextSub
) {
  const [, forceRender] = useReducer(s => s + 1, 0)

  const subscription = useMemo(() => new Subscription(store, contextSub), [
    store,
    contextSub
  ])

  const latestSubscriptionCallbackError = useRef()
  const latestSelector = useRef()
  const latestSelectedState = useRef()

  let selectedState

  try {
    if (
      selector !== latestSelector.current ||
      latestSubscriptionCallbackError.current
    ) {
      selectedState = selector(store.getState())
    } else {
      selectedState = latestSelectedState.current
    }
  } catch (err) {
    let errorMessage = `An error occurred while selecting the store state: ${err.message}.`

    if (latestSubscriptionCallbackError.current) {
      errorMessage += `\nThe error may be correlated with this previous error:\n${latestSubscriptionCallbackError.current.stack}\n\nOriginal stack trace:`
    }

    throw new Error(errorMessage)
  }

  useIsomorphicLayoutEffect(() => {
    latestSelector.current = selector
    latestSelectedState.current = selectedState
    latestSubscriptionCallbackError.current = undefined
  })

  useIsomorphicLayoutEffect(() => {
    function checkForUpdates() {
      try {
        const newSelectedState = latestSelector.current(store.getState())

        if (equalityFn(newSelectedState, latestSelectedState.current)) {
          return
        }

        latestSelectedState.current = newSelectedState
      } catch (err) {
        // we ignore all errors here, since when the component
        // is re-rendered, the selectors are called again, and
        // will throw again, if neither props nor store state
        // changed
        latestSubscriptionCallbackError.current = err
      }

      forceRender({})
    }

    subscription.onStateChange = checkForUpdates
    subscription.trySubscribe()

    checkForUpdates()

    return () => subscription.tryUnsubscribe()
  }, [store, subscription])

  return selectedState
}

/**
 * Hook factory, which creates a `useSelector` hook bound to a given context.
 *
 * @param {Function} [context=ReactReduxContext] Context passed to your `<Provider>`.
 * @returns {Function} A `useSelector` hook bound to the specified context.
 */
export function createSelectorHook(context = ReactReduxContext) {
  const useReduxContext =
    context === ReactReduxContext
      ? useDefaultReduxContext
      : () => useContext(context)
  return function useSelector(selector, equalityFn = refEquality) {
    invariant(selector, `You must pass a selector to useSelectors`)

    const { store, subscription: contextSub } = useReduxContext()

    return useSelectorWithStoreAndSubscription(
      selector,
      equalityFn,
      store,
      contextSub
    )
  }
}

/**
 * A hook to access the redux store's state. This hook takes a selector function
 * as an argument. The selector is called with the store state.
 *
 * This hook takes an optional equality comparison function as the second parameter
 * that allows you to customize the way the selected state is compared to determine
 * whether the component needs to be re-rendered.
 *
 * @param {Function} selector the selector function
 * @param {Function=} equalityFn the function that will be used to determine equality
 *
 * @returns {any} the selected state
 *
 * @example
 *
 * import React from 'react'
 * import { useSelector } from 'react-redux'
 *
 * export const CounterComponent = () => {
 *   const counter = useSelector(state => state.counter)
 *   return
{counter}

 * }
 */
export const useSelector = createSelectorHook()
<|endoftext|>


<commit_before>/* @flow */
import React from 'react';
import entities from 'entities';

import type { StyleObj } from '../types';
import { RawLabel } from '../common';

type Props = {
  data: string,
  cascadingTextStyle?: StyleObj,
  splitMessageText?: boolean,
};

export default ({ data, cascadingTextStyle, splitMessageText }: Props) =>
  splitMessageText ? (
    entities
      .decodeHTML(data)
      .replace(/\n/, '')
      .split(' ')
      .map(text => (
        <RawLabel
          key={Math.random()
            .toString(36)
            .substring(7)}
          style={cascadingTextStyle}
          text={`${text} `}
        />
      ))
  ) : (
    <RawLabel style={cascadingTextStyle} text={entities.decodeHTML(data).replace(/\n/, '')} />
  );
<commit_msg>message render: Don't join last word with space. (#1443)<commit_after>/* @flow */
import React from 'react';
import entities from 'entities';

import type { StyleObj } from '../types';
import { RawLabel } from '../common';

type Props = {
  data: string,
  cascadingTextStyle?: StyleObj,
  splitMessageText?: boolean,
};

export default ({ data, cascadingTextStyle, splitMessageText }: Props) => {
  if (splitMessageText) {
    const textItems = entities
      .decodeHTML(data)
      .replace(/\n/, '')
      .split(' ');
    const itemsCount = textItems.length;
    return textItems.map((text, index) => (
      <RawLabel
        key={Math.random()
          .toString(36)
          .substring(7)}
        style={cascadingTextStyle}
        text={itemsCount === index + 1 ? text : `${text} `}
      />
    ));
  }
  return <RawLabel style={cascadingTextStyle} text={entities.decodeHTML(data).replace(/\n/, '')} />;
};
<|endoftext|>


<commit_before>import bodyParser from 'body-parser'
import cors from 'cors'
import errorHandler from 'errorhandler'
import express from 'express'
import helmet from 'helmet'
import methodOverride from 'method-override'
import morgan from 'morgan'
import routes from './routes'
import settings from '../configuration'

let app = express()

// Adds some security best practices
app.use(helmet())
app.use(cors())

// Logger
if (!settings.envs.test) {
    app.use(morgan('dev'))
}

// Properly Decode JSON
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: true }))

// Add all HTTP methods
app.use(methodOverride())

// Mount API routes
app.use('/', routes)

// Only use error handler in development
if (settings.envs.development) {
    app.use(errorHandler())
}

app.listen(settings.port, () => {
    console.log(`
    === App Server ===

    Connected on:

    Port: ${settings.port}
    Env: ${app.get('env')}

  `)
})

export default app<commit_msg>Minor tweak<commit_after>import bodyParser from 'body-parser'
import cors from 'cors'
import errorHandler from 'errorhandler'
import express from 'express'
import helmet from 'helmet'
import methodOverride from 'method-override'
import morgan from 'morgan'
import routes from './routes'
import settings from '../configuration'

const app = express()

// Adds some security best practices
app.use(helmet())
app.use(cors())

// Logger
if (!settings.envs.test) {
    app.use(morgan('dev'))
}

// Properly Decode JSON
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: true }))

// Add all HTTP methods
app.use(methodOverride())

// Mount API routes
app.use('/', routes)

// Only use error handler in development
if (settings.envs.development) {
    app.use(errorHandler())
}

app.listen(settings.port, () => {
    console.log(`
    === App Server ===

    Connected on:

    Port: ${settings.port}
    Env: ${app.get('env')}

  `)
})

export default app<|endoftext|>


<commit_before>// Copyright (c) 2015, Evan Summers (twitter.com/evanxsummers)
// ISC license, see http://github.com/evanx/redexutil/LICENSE

const logger = Loggers.create(__filename, 'info');

export function formatEmpty(string, defaultString) {
   if (string) {
      return string;
   } else {
      return defaultString || '';
   }
}

export function formatNullable(string) { // deprecated
   return formatEmpty(string);
}

export function padLeftZero(value, length) {
   let string = value.toString();
   while (string.length < length) {
      string = '0' + string;
   }
   return string;
}

export function joinColon() { // deprecated
   return Array.prototype.slice.call(arguments).join(':');
}

export function extractRegex(string, regex) {
   let match = string.match(regex);
   if (match && match.length > 1) {
      return match[1];
   }
}
<commit_msg>regex<commit_after>// Copyright (c) 2015, Evan Summers (twitter.com/evanxsummers)
// ISC license, see http://github.com/evanx/redexutil/LICENSE

const logger = Loggers.create(__filename, 'info');

export function formatEmpty(string, defaultString) {
  if (string) {
    return string;
  } else {
    return defaultString || '';
  }
}

export function formatNullable(string) { // deprecated
  return formatEmpty(string);
}

export function padLeftZero(value, length) {
  let string = value.toString();
  while (string.length < length) {
    string = '0' + string;
  }
  return string;
}

export function joinColon() { // deprecated
  return Array.prototype.slice.call(arguments).join(':');
}

export function extractRegex(string, regex) {
  if (string) {
    let match = string.match(regex);
    if (match && match.length > 1) {
      return match[1];
    }
  }
}
<|endoftext|>