<commit_before>/* jshint -W084 */
(function(cledit) {

	function SelectionMgr(editor) {
		var debounce = cledit.Utils.debounce;
		var contentElt = editor.$contentElt;
		var scrollElt = editor.$scrollElt;
		cledit.Utils.createEventHooks(this);

		var self = this;
		var lastSelectionStart = 0,
			lastSelectionEnd = 0;
		this.selectionStart = 0;
		this.selectionEnd = 0;
		this.cursorCoordinates = {};
		this.adjustTop = 0;
		this.adjustBottom = 0;

		this.findContainer = function(offset) {
			var result = cledit.Utils.findContainer(contentElt, offset);
			if (result.container.nodeValue === '\n') {
				var hdLfElt = result.container.parentNode;
				if (hdLfElt.className === 'hd-lf' && hdLfElt.previousSibling && hdLfElt.previousSibling.tagName === 'BR') {
					result.container = hdLfElt.parentNode;
					result.offsetInContainer = Array.prototype.indexOf.call(result.container.childNodes, result.offsetInContainer === 0 ? hdLfElt.previousSibling : hdLfElt);
				}
			}
			return result;
		};

		this.createRange = function(start, end) {
			var range = editor.$document.createRange();
			if (start === end) {
				end = start = isNaN(start) ? start : this.findContainer(start < 0 ? 0 : start);
			} else {
				start = isNaN(start) ? start : this.findContainer(start < 0 ? 0 : start);
				end = isNaN(end) ? end : this.findContainer(end < 0 ? 0 : end);
			}
			range.setStart(start.container, start.offsetInContainer);
			range.setEnd(end.container, end.offsetInContainer);
			return range;
		};

		var adjustScroll;
		var debouncedUpdateCursorCoordinates = debounce((function() {
			var coordinates = this.getCoordinates(this.selectionEnd, this.selectionEndContainer, this.selectionEndOffset);
			if (this.cursorCoordinates.top !== coordinates.top ||
				this.cursorCoordinates.height !== coordinates.height ||
				this.cursorCoordinates.left !== coordinates.left
			) {
				this.cursorCoordinates = coordinates;
				this.$trigger('cursorCoordinatesChanged', coordinates);
			}
			if (adjustScroll) {
				var adjustTop, adjustBottom;
				adjustTop = adjustBottom = scrollElt.clientHeight / 2 * editor.options.cursorFocusRatio;
				adjustTop = this.adjustTop || adjustTop;
				adjustBottom = this.adjustBottom || adjustTop;
				if (adjustTop && adjustBottom) {
					var cursorMinY = scrollElt.scrollTop + adjustTop;
					var cursorMaxY = scrollElt.scrollTop + scrollElt.clientHeight - adjustBottom;
					if (this.cursorCoordinates.top < cursorMinY) {
						scrollElt.scrollTop += this.cursorCoordinates.top - cursorMinY;
					} else if (this.cursorCoordinates.top + this.cursorCoordinates.height > cursorMaxY) {
						scrollElt.scrollTop += this.cursorCoordinates.top + this.cursorCoordinates.height - cursorMaxY;
					}
				}
			}
			adjustScroll = false;
		}).bind(this));

		this.updateCursorCoordinates = function(adjustScrollParam) {
			adjustScroll = adjustScroll || adjustScrollParam;
			debouncedUpdateCursorCoordinates();
		};

		var oldSelectionRange;

		function checkSelection(selectionRange) {
			if (!oldSelectionRange ||
				oldSelectionRange.startContainer !== selectionRange.startContainer ||
				oldSelectionRange.startOffset !== selectionRange.startOffset ||
				oldSelectionRange.endContainer !== selectionRange.endContainer ||
				oldSelectionRange.endOffset !== selectionRange.endOffset
			) {
				oldSelectionRange = selectionRange;
				self.$trigger('selectionChanged', self.selectionStart, self.selectionEnd, selectionRange);
				return true;
			}
		}

		this.restoreSelection = function() {
			var min = Math.min(this.selectionStart, this.selectionEnd);
			var max = Math.max(this.selectionStart, this.selectionEnd);
			var selectionRange = this.createRange(min, max);
			var selection = editor.$window.getSelection();
			selection.removeAllRanges();
			var isBackward = this.selectionStart > this.selectionEnd;
			if (isBackward && selection.extend) {
				var endRange = selectionRange.cloneRange();
				endRange.collapse(false);
				selection.addRange(endRange);
				selection.extend(selectionRange.startContainer, selectionRange.startOffset);
			} else {
				selection.addRange(selectionRange);
			}
			checkSelection(selectionRange);
			return selectionRange;
		};

		var saveLastSelection = debounce(function() {
			lastSelectionStart = self.selectionStart;
			lastSelectionEnd = self.selectionEnd;
		}, 50);

		function setSelection(start, end) {
			if (start === undefined) {
				start = self.selectionStart;
			}
			if (start < 0) {
				start = 0;
			}
			if (end === undefined) {
				end = this.selectionEnd;
			}
			if (end < 0) {
				end = 0;
			}
			self.selectionStart = start;
			self.selectionEnd = end;
			saveLastSelection();
		}

		this.setSelectionStartEnd = function(start, end) {
			setSelection(start, end);
			return this.restoreSelection();
		};

		this.saveSelectionState = (function() {

			// Credit: https://github.com/timdown/rangy
			function arrayContains(arr, val) {
				var i = arr.length;
				while (i--) {
					if (arr[i] === val) {
						return true;
					}
				}
				return false;
			}

			function getClosestAncestorIn(node, ancestor, selfIsAncestor) {
				var p, n = selfIsAncestor ? node : node.parentNode;
				while (n) {
					p = n.parentNode;
					if (p === ancestor) {
						return n;
					}
					n = p;
				}
				return null;
			}

			function getNodeIndex(node) {
				var i = 0;
				while ((node = node.previousSibling)) {
					++i;
				}
				return i;
			}

			function getCommonAncestor(node1, node2) {
				var ancestors = [],
					n;
				for (n = node1; n; n = n.parentNode) {
					ancestors.push(n);
				}

				for (n = node2; n; n = n.parentNode) {
					if (arrayContains(ancestors, n)) {
						return n;
					}
				}

				return null;
			}

			function comparePoints(nodeA, offsetA, nodeB, offsetB) {
				// See http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-Range-Comparing
				var nodeC, root, childA, childB, n;
				if (nodeA == nodeB) {
					// Case 1: nodes are the same
					return offsetA === offsetB ? 0 : (offsetA < offsetB) ? -1 : 1;
				} else if ((nodeC = getClosestAncestorIn(nodeB, nodeA, true))) {
					// Case 2: node C (container B or an ancestor) is a child node of A
					return offsetA <= getNodeIndex(nodeC) ? -1 : 1;
				} else if ((nodeC = getClosestAncestorIn(nodeA, nodeB, true))) {
					// Case 3: node C (container A or an ancestor) is a child node of B
					return getNodeIndex(nodeC) < offsetB ? -1 : 1;
				} else {
					root = getCommonAncestor(nodeA, nodeB);
					if (!root) {
						throw new Error("comparePoints error: nodes have no common ancestor");
					}

					// Case 4: containers are siblings or descendants of siblings
					childA = (nodeA === root) ? root : getClosestAncestorIn(nodeA, root, true);
					childB = (nodeB === root) ? root : getClosestAncestorIn(nodeB, root, true);

					if (childA === childB) {
						// This shouldn't be possible
						throw module.createError("comparePoints got to case 4 and childA and childB are the same!");
					} else {
						n = root.firstChild;
						while (n) {
							if (n === childA) {
								return -1;
							} else if (n === childB) {
								return 1;
							}
							n = n.nextSibling;
						}
					}
				}
			}

			function save() {
				var selectionStart = self.selectionStart;
				var selectionEnd = self.selectionEnd;
				var selection = editor.$window.getSelection();
				var result;
				if (selection.rangeCount > 0) {
					var selectionRange = selection.getRangeAt(0);
					var node = selectionRange.startContainer;
					if ((contentElt.compareDocumentPosition(node) & Node.DOCUMENT_POSITION_CONTAINED_BY) || contentElt === node) {
						var offset = selectionRange.startOffset;
						if (node.firstChild && offset > 0) {
							node = node.childNodes[offset - 1];
							offset = node.textContent.length;
						}
						var container = node;
						while (node != contentElt) {
							while (node = node.previousSibling) {
								offset += (node.textContent || '').length;
							}
							node = container = container.parentNode;
						}
						var selectionText = selectionRange + '';
						// Fix end of line when only br is selected
						var brElt = selectionRange.endContainer.firstChild;
						if (brElt && brElt.tagName === 'BR' && selectionRange.endOffset === 1) {
							selectionText += '\n';
						}
						if (comparePoints(selection.anchorNode, selection.anchorOffset, selection.focusNode, selection.focusOffset) == 1) {
							selectionStart = offset + selectionText.length;
							selectionEnd = offset;
						} else {
							selectionStart = offset;
							selectionEnd = offset + selectionText.length;
						}

						if (selectionStart === selectionEnd && selectionStart === editor.getContent().length) {
							// If cursor is after the trailingNode
							selectionStart = --selectionEnd;
							result = self.setSelectionStartEnd(selectionStart, selectionEnd);
						} else {
							setSelection(selectionStart, selectionEnd);
							result = checkSelection(selectionRange);
							result = result || lastSelectionStart !== self.selectionStart; // selectionRange doesn't change when selection is at the start of a section
						}
					}
				}
				return result;
			}

			function saveCheckChange() {
				return save() && (lastSelectionStart !== self.selectionStart || lastSelectionEnd !== self.selectionEnd);
			}

			var nextTickAdjustScroll = false;
			var debouncedSave = debounce(function() {
				self.updateCursorCoordinates(saveCheckChange() && nextTickAdjustScroll);
				// In some cases we have to wait a little longer to see the selection change (Cmd+A on Chrome OSX)
				longerDebouncedSave();
			});
			var longerDebouncedSave = debounce(function() {
				self.updateCursorCoordinates(saveCheckChange() && nextTickAdjustScroll);
				nextTickAdjustScroll = false;
			}, 10);

			return function(debounced, adjustScroll, forceAdjustScroll) {
				if (forceAdjustScroll) {
					lastSelectionStart = undefined;
					lastSelectionEnd = undefined;
				}
				if (debounced) {
					nextTickAdjustScroll = nextTickAdjustScroll || adjustScroll;
					return debouncedSave();
				} else {
					save();
				}
			};
		})();

		this.getSelectedText = function() {
			var min = Math.min(this.selectionStart, this.selectionEnd);
			var max = Math.max(this.selectionStart, this.selectionEnd);
			return editor.getContent().substring(min, max);
		};

		this.getCoordinates = function(inputOffset, container, offsetInContainer) {
			if (!container) {
				var offset = this.findContainer(inputOffset);
				container = offset.container;
				offsetInContainer = offset.offsetInContainer;
			}
			var containerElt = container;
			if (!containerElt.hasChildNodes()) {
				containerElt = container.parentNode;
			}
			var isInvisible = false;
			var index = editor.$allElements.indexOf(containerElt);
			while (containerElt.offsetHeight === 0 && index > 0) {
				isInvisible = true;
				containerElt = editor.$allElements[--index];
			}
			if (isInvisible || container.textContent == '\n') {
				return {
					top: containerElt.offsetTop,
					height: containerElt.offsetHeight,
					left: containerElt.offsetLeft
				};
			} else {
				var selectedChar = editor.getContent()[inputOffset];
				var startOffset = {
					container: container,
					offsetInContainer: offsetInContainer
				};
				var endOffset = {
					container: container,
					offsetInContainer: offsetInContainer
				};
				var left = 'left';
				if (inputOffset > 0 && (selectedChar === undefined || selectedChar == '\n')) {
					left = 'right';
					if (startOffset.offsetInContainer === 0) {
						// Need to calculate offset-1
						startOffset = inputOffset - 1;
					} else {
						startOffset.offsetInContainer -= 1;
					}
				} else {
					if (endOffset.offset === container.textContent.length) {
						// Need to calculate offset+1
						endOffset = inputOffset + 1;
					} else {
						endOffset.offsetInContainer += 1;
					}
				}
				var range = this.createRange(startOffset, endOffset);
				var rect = range.getBoundingClientRect();
				return {
					top: Math.round(rect.top - contentElt.getBoundingClientRect().top + contentElt.scrollTop),
					height: Math.round(rect.height),
					left: Math.round(rect[left] - contentElt.getBoundingClientRect().left + contentElt.scrollLeft)
				};
			}
		};

		this.getClosestWordOffset = function(offset) {
			var offsetStart = 0;
			var offsetEnd = 0;
			var nextOffset = 0;
			editor.getContent().split(/\s/).some(function(word) {
				if (word) {
					offsetStart = nextOffset;
					offsetEnd = nextOffset + word.length;
					if (offsetEnd > offset) {
						return true;
					}
				}
				nextOffset += word.length + 1;
			});
			return {
				start: offsetStart,
				end: offsetEnd
			};
		};
	}

	cledit.SelectionMgr = SelectionMgr;

})(window.cledit);
<commit_msg>Fixed end of line cursor coordinates<commit_after>/* jshint -W084 */
(function(cledit) {

	function SelectionMgr(editor) {
		var debounce = cledit.Utils.debounce;
		var contentElt = editor.$contentElt;
		var scrollElt = editor.$scrollElt;
		cledit.Utils.createEventHooks(this);

		var self = this;
		var lastSelectionStart = 0,
			lastSelectionEnd = 0;
		this.selectionStart = 0;
		this.selectionEnd = 0;
		this.cursorCoordinates = {};
		this.adjustTop = 0;
		this.adjustBottom = 0;

		this.findContainer = function(offset) {
			var result = cledit.Utils.findContainer(contentElt, offset);
			if (result.container.nodeValue === '\n') {
				var hdLfElt = result.container.parentNode;
				if (hdLfElt.className === 'hd-lf' && hdLfElt.previousSibling && hdLfElt.previousSibling.tagName === 'BR') {
					result.container = hdLfElt.parentNode;
					result.offsetInContainer = Array.prototype.indexOf.call(result.container.childNodes, result.offsetInContainer === 0 ? hdLfElt.previousSibling : hdLfElt);
				}
			}
			return result;
		};

		this.createRange = function(start, end) {
			var range = editor.$document.createRange();
			if (start === end) {
				end = start = isNaN(start) ? start : this.findContainer(start < 0 ? 0 : start);
			} else {
				start = isNaN(start) ? start : this.findContainer(start < 0 ? 0 : start);
				end = isNaN(end) ? end : this.findContainer(end < 0 ? 0 : end);
			}
			range.setStart(start.container, start.offsetInContainer);
			range.setEnd(end.container, end.offsetInContainer);
			return range;
		};

		var adjustScroll;
		var debouncedUpdateCursorCoordinates = debounce((function() {
			var coordinates = this.getCoordinates(this.selectionEnd, this.selectionEndContainer, this.selectionEndOffset);
			if (this.cursorCoordinates.top !== coordinates.top ||
				this.cursorCoordinates.height !== coordinates.height ||
				this.cursorCoordinates.left !== coordinates.left
			) {
				this.cursorCoordinates = coordinates;
				this.$trigger('cursorCoordinatesChanged', coordinates);
			}
			if (adjustScroll) {
				var adjustTop, adjustBottom;
				adjustTop = adjustBottom = scrollElt.clientHeight / 2 * editor.options.cursorFocusRatio;
				adjustTop = this.adjustTop || adjustTop;
				adjustBottom = this.adjustBottom || adjustTop;
				if (adjustTop && adjustBottom) {
					var cursorMinY = scrollElt.scrollTop + adjustTop;
					var cursorMaxY = scrollElt.scrollTop + scrollElt.clientHeight - adjustBottom;
					if (this.cursorCoordinates.top < cursorMinY) {
						scrollElt.scrollTop += this.cursorCoordinates.top - cursorMinY;
					} else if (this.cursorCoordinates.top + this.cursorCoordinates.height > cursorMaxY) {
						scrollElt.scrollTop += this.cursorCoordinates.top + this.cursorCoordinates.height - cursorMaxY;
					}
				}
			}
			adjustScroll = false;
		}).bind(this));

		this.updateCursorCoordinates = function(adjustScrollParam) {
			adjustScroll = adjustScroll || adjustScrollParam;
			debouncedUpdateCursorCoordinates();
		};

		var oldSelectionRange;

		function checkSelection(selectionRange) {
			if (!oldSelectionRange ||
				oldSelectionRange.startContainer !== selectionRange.startContainer ||
				oldSelectionRange.startOffset !== selectionRange.startOffset ||
				oldSelectionRange.endContainer !== selectionRange.endContainer ||
				oldSelectionRange.endOffset !== selectionRange.endOffset
			) {
				oldSelectionRange = selectionRange;
				self.$trigger('selectionChanged', self.selectionStart, self.selectionEnd, selectionRange);
				return true;
			}
		}

		this.restoreSelection = function() {
			var min = Math.min(this.selectionStart, this.selectionEnd);
			var max = Math.max(this.selectionStart, this.selectionEnd);
			var selectionRange = this.createRange(min, max);
			var selection = editor.$window.getSelection();
			selection.removeAllRanges();
			var isBackward = this.selectionStart > this.selectionEnd;
			if (isBackward && selection.extend) {
				var endRange = selectionRange.cloneRange();
				endRange.collapse(false);
				selection.addRange(endRange);
				selection.extend(selectionRange.startContainer, selectionRange.startOffset);
			} else {
				selection.addRange(selectionRange);
			}
			checkSelection(selectionRange);
			return selectionRange;
		};

		var saveLastSelection = debounce(function() {
			lastSelectionStart = self.selectionStart;
			lastSelectionEnd = self.selectionEnd;
		}, 50);

		function setSelection(start, end) {
			if (start === undefined) {
				start = self.selectionStart;
			}
			if (start < 0) {
				start = 0;
			}
			if (end === undefined) {
				end = this.selectionEnd;
			}
			if (end < 0) {
				end = 0;
			}
			self.selectionStart = start;
			self.selectionEnd = end;
			saveLastSelection();
		}

		this.setSelectionStartEnd = function(start, end) {
			setSelection(start, end);
			return this.restoreSelection();
		};

		this.saveSelectionState = (function() {

			// Credit: https://github.com/timdown/rangy
			function arrayContains(arr, val) {
				var i = arr.length;
				while (i--) {
					if (arr[i] === val) {
						return true;
					}
				}
				return false;
			}

			function getClosestAncestorIn(node, ancestor, selfIsAncestor) {
				var p, n = selfIsAncestor ? node : node.parentNode;
				while (n) {
					p = n.parentNode;
					if (p === ancestor) {
						return n;
					}
					n = p;
				}
				return null;
			}

			function getNodeIndex(node) {
				var i = 0;
				while ((node = node.previousSibling)) {
					++i;
				}
				return i;
			}

			function getCommonAncestor(node1, node2) {
				var ancestors = [],
					n;
				for (n = node1; n; n = n.parentNode) {
					ancestors.push(n);
				}

				for (n = node2; n; n = n.parentNode) {
					if (arrayContains(ancestors, n)) {
						return n;
					}
				}

				return null;
			}

			function comparePoints(nodeA, offsetA, nodeB, offsetB) {
				// See http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-Range-Comparing
				var nodeC, root, childA, childB, n;
				if (nodeA == nodeB) {
					// Case 1: nodes are the same
					return offsetA === offsetB ? 0 : (offsetA < offsetB) ? -1 : 1;
				} else if ((nodeC = getClosestAncestorIn(nodeB, nodeA, true))) {
					// Case 2: node C (container B or an ancestor) is a child node of A
					return offsetA <= getNodeIndex(nodeC) ? -1 : 1;
				} else if ((nodeC = getClosestAncestorIn(nodeA, nodeB, true))) {
					// Case 3: node C (container A or an ancestor) is a child node of B
					return getNodeIndex(nodeC) < offsetB ? -1 : 1;
				} else {
					root = getCommonAncestor(nodeA, nodeB);
					if (!root) {
						throw new Error("comparePoints error: nodes have no common ancestor");
					}

					// Case 4: containers are siblings or descendants of siblings
					childA = (nodeA === root) ? root : getClosestAncestorIn(nodeA, root, true);
					childB = (nodeB === root) ? root : getClosestAncestorIn(nodeB, root, true);

					if (childA === childB) {
						// This shouldn't be possible
						throw module.createError("comparePoints got to case 4 and childA and childB are the same!");
					} else {
						n = root.firstChild;
						while (n) {
							if (n === childA) {
								return -1;
							} else if (n === childB) {
								return 1;
							}
							n = n.nextSibling;
						}
					}
				}
			}

			function save() {
				var selectionStart = self.selectionStart;
				var selectionEnd = self.selectionEnd;
				var selection = editor.$window.getSelection();
				var result;
				if (selection.rangeCount > 0) {
					var selectionRange = selection.getRangeAt(0);
					var node = selectionRange.startContainer;
					if ((contentElt.compareDocumentPosition(node) & Node.DOCUMENT_POSITION_CONTAINED_BY) || contentElt === node) {
						var offset = selectionRange.startOffset;
						if (node.firstChild && offset > 0) {
							node = node.childNodes[offset - 1];
							offset = node.textContent.length;
						}
						var container = node;
						while (node != contentElt) {
							while (node = node.previousSibling) {
								offset += (node.textContent || '').length;
							}
							node = container = container.parentNode;
						}
						var selectionText = selectionRange + '';
						// Fix end of line when only br is selected
						var brElt = selectionRange.endContainer.firstChild;
						if (brElt && brElt.tagName === 'BR' && selectionRange.endOffset === 1) {
							selectionText += '\n';
						}
						if (comparePoints(selection.anchorNode, selection.anchorOffset, selection.focusNode, selection.focusOffset) == 1) {
							selectionStart = offset + selectionText.length;
							selectionEnd = offset;
						} else {
							selectionStart = offset;
							selectionEnd = offset + selectionText.length;
						}

						if (selectionStart === selectionEnd && selectionStart === editor.getContent().length) {
							// If cursor is after the trailingNode
							selectionStart = --selectionEnd;
							result = self.setSelectionStartEnd(selectionStart, selectionEnd);
						} else {
							setSelection(selectionStart, selectionEnd);
							result = checkSelection(selectionRange);
							result = result || lastSelectionStart !== self.selectionStart; // selectionRange doesn't change when selection is at the start of a section
						}
					}
				}
				return result;
			}

			function saveCheckChange() {
				return save() && (lastSelectionStart !== self.selectionStart || lastSelectionEnd !== self.selectionEnd);
			}

			var nextTickAdjustScroll = false;
			var debouncedSave = debounce(function() {
				self.updateCursorCoordinates(saveCheckChange() && nextTickAdjustScroll);
				// In some cases we have to wait a little longer to see the selection change (Cmd+A on Chrome OSX)
				longerDebouncedSave();
			});
			var longerDebouncedSave = debounce(function() {
				self.updateCursorCoordinates(saveCheckChange() && nextTickAdjustScroll);
				nextTickAdjustScroll = false;
			}, 10);

			return function(debounced, adjustScroll, forceAdjustScroll) {
				if (forceAdjustScroll) {
					lastSelectionStart = undefined;
					lastSelectionEnd = undefined;
				}
				if (debounced) {
					nextTickAdjustScroll = nextTickAdjustScroll || adjustScroll;
					return debouncedSave();
				} else {
					save();
				}
			};
		})();

		this.getSelectedText = function() {
			var min = Math.min(this.selectionStart, this.selectionEnd);
			var max = Math.max(this.selectionStart, this.selectionEnd);
			return editor.getContent().substring(min, max);
		};

		this.getCoordinates = function(inputOffset, container, offsetInContainer) {
			if (!container) {
				var offset = this.findContainer(inputOffset);
				container = offset.container;
				offsetInContainer = offset.offsetInContainer;
			}
			var containerElt = container;
			if (!containerElt.hasChildNodes()) {
				containerElt = container.parentNode;
			}
			var isInvisible = false;
			var index = editor.$allElements.indexOf(containerElt);
			while (containerElt.offsetHeight === 0 && index > 0) {
				isInvisible = true;
				containerElt = editor.$allElements[--index];
			}
			var rect,
				contentRect,
				left = 'left';
			if (isInvisible || container.textContent == '\n') {
				rect = containerElt.getBoundingClientRect();
			} else {
				var selectedChar = editor.getContent()[inputOffset];
				var startOffset = {
					container: container,
					offsetInContainer: offsetInContainer
				};
				var endOffset = {
					container: container,
					offsetInContainer: offsetInContainer
				};
				if (inputOffset > 0 && (selectedChar === undefined || selectedChar == '\n')) {
					left = 'right';
					if (startOffset.offsetInContainer === 0) {
						// Need to calculate offset-1
						startOffset = inputOffset - 1;
					} else {
						startOffset.offsetInContainer -= 1;
					}
				} else {
					if (endOffset.offset === container.textContent.length) {
						// Need to calculate offset+1
						endOffset = inputOffset + 1;
					} else {
						endOffset.offsetInContainer += 1;
					}
				}
				var range = this.createRange(startOffset, endOffset);
				rect = range.getBoundingClientRect();
			}
			contentRect = contentElt.getBoundingClientRect();
			return {
				top: Math.round(rect.top - contentRect.top + contentElt.scrollTop),
				height: Math.round(rect.height),
				left: Math.round(rect[left] - contentRect.left + contentElt.scrollLeft)
			};
		};

		this.getClosestWordOffset = function(offset) {
			var offsetStart = 0;
			var offsetEnd = 0;
			var nextOffset = 0;
			editor.getContent().split(/\s/).some(function(word) {
				if (word) {
					offsetStart = nextOffset;
					offsetEnd = nextOffset + word.length;
					if (offsetEnd > offset) {
						return true;
					}
				}
				nextOffset += word.length + 1;
			});
			return {
				start: offsetStart,
				end: offsetEnd
			};
		};
	}

	cledit.SelectionMgr = SelectionMgr;

})(window.cledit);
<|endoftext|>


<commit_before>(function(angular){

    var app = angular.module('new-course');

    app.controller('CourseEditController',
        ['$scope', '$window', '$uibModal', '$http', '$q', 'Course',  'CourseAuthor', 'Lesson', '$filter', 'youtubePlayerApi', 'VideoData', 'FormUpload', 'uibDateParser',
        function($scope, $window, $uibModal, $http , $q, Course,  CourseProfessor, Lesson, $filter, youtubePlayerApi, VideoData, FormUpload, uibDateParser) {

            $scope.errors = {};
            var httpErrors = {
                '400': 'Os campos não foram preenchidos corretamente.',
                '403': 'Você não tem permissão para ver conteúdo nesta página.',
                '404': 'Este curso não existe!'
            };

            $scope.course_id = parseInt($window.course_id, 10);

            $scope.courseProfessors = [];
            $scope.lessons = [];

            Course.get({id: $scope.course_id}, function(course){
                if(course.intro_video) {
                    youtubePlayerApi.videoId = course.intro_video.youtube_id;
                }
                document.title = 'Curso: {0}'.format(course.name);
                $scope.addThumb = !course.thumbnail_url;
                $scope.addHomeThumb = !course.home_thumbnail_url;
                course.start_date = uibDateParser.parse(course.start_date, "yyyy-MM-dd");
                $scope.course = course;
            });

            $scope.lessons = Lesson.query({
              'course__id': $scope.course_id
            });

            $scope.courseProfessors = CourseProfessor.query({
              course: $scope.course_id
            });

            var player;
            $scope.playerReady = false;
            youtubePlayerApi.loadPlayer().then(function(p){
                player = p;
                $scope.playerReady = true;
            });

            $scope.$watch('course.intro_video.youtube_id', function(vid, oldVid){
                if(!vid || vid === oldVid) return;
                if(player) player.cueVideoById(vid);
                VideoData.load(vid).then(function(data){
                    if (data.items.length > 0)
                        $scope.course.intro_video.name = data.items[0].snippet.title;
                });
            });

            $scope.setCurrentUnitVideo = function() {
                //
                // support pasting both long and short urls from youtube
                // eg. http://youtu.be/8uj7YSqby7s
                //
                var complete_url = /^.*(youtu\.be\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=)([^#\&\?]*).*/;
                var result = complete_url.exec($scope.youtube_id);
                if (result && result[2].length == 11) {
                    $scope.course.intro_video.youtube_id = result[2];
                }
            };

            function showFieldErrors(response) {
                $scope.errors = response.data;
                var messages = [];
                for(var att in response.data) {
                    var message = response.data[att];
                    if(Course.fields && Course.fields[att]) {
                        message = Course.fields[att].label + ': ' + message;
                    }
                    messages.push(message);
                }
                $scope.alert.error('Encontramos alguns erros!', messages, true);
            }

            $scope.saveThumb = function() {
                if(! $scope.thumbfile) {
                    return;
                }

                if ($scope.course.id) {
                    var fu = new FormUpload();
                    fu.addField('thumbnail', $scope.thumbfile);
                    // return a new promise that file will be uploaded
                    return fu.sendTo('/api/coursethumbs/' + $scope.course.id)
                        .then(function(){
                            $scope.alert.success('A imagem atualizada.');
                        });
                }
            };

            $scope.saveHomeThumb = function() {
                if(! $scope.home_thumbfile) {
                    return;
                }

                if ($scope.course.id) {
                    var fu = new FormUpload();
                    fu.addField('home_thumbnail', $scope.home_thumbfile);
                    // return a new promise that file will be uploaded
                    return fu.sendTo('/api/coursethumbs/' + $scope.course.id)
                        .then(function(){
                            $scope.alert.success('A imagem atualizada.');
                        });
                }
            };

            $scope.export_course = function() {
                $window.open('/admin/course/' + $scope.course_id + '/export/', '_blank', '');
            };

            $scope.saveCourse = function() {
                if(!$scope.course.hasVideo()){
                    delete $scope.course.intro_video;
                }
                if(!$scope.course.slug){
                    $scope.course.slug = $filter('slugify')($scope.course.name);
                }

                if ($scope.course.start_date) {
                    $scope.course.start_date = $filter('date')($scope.course.start_date, 'yyyy-MM-dd');
                }

                $scope.course.save()
                    .then(function(course){
                        course.start_date = uibDateParser.parse(course.start_date, "yyyy-MM-dd");
                        return $scope.saveThumb();
                    })
                    .then(function(){
                        $scope.alert.success('Alterações salvas com sucesso!');
                    })['catch'](showFieldErrors);
            };

            $scope.publishCourse = function() {
                $scope.course.status = 'published';
                $scope.saveCourse();
            };

            $scope.set_course_as_draft = function() {
                if (confirm('Ao tornar o curso um rascunho, ele não será mais visível para os usuários! Tem certeza de que quer continuar?')){
                    $scope.course.status = 'draft';
                    $scope.saveCourse();
                }
            };

            $scope.deleteCourse = function() {
                if(!confirm('Tem certeza que deseja remover este curso?')) return;

                $scope.course.$delete()
                    .then(function(){
                        document.location.href = '/admin/courses';
                    });
            };

            $scope.delete_instructor = function(course_author) {

                var confirm_exclude_instructor_msg = 'Tem certeza que deseja remover "{0}" da lista de instrutores deste curso?'.format(course_author.get_name);
                if (window.confirm(confirm_exclude_instructor_msg)){
                    course_author.$delete().then(function(){
                        var index = $scope.courseProfessors.indexOf(course_author);
                        $scope.courseProfessors.splice(index, 1);
                        $scope.alert.success('"{0}" foi removido da lista de instrutores.'.format(course_author.get_name));
                    }, function(){
                        $scope.alert.error('Erro ao remover "{0}" da lista de instrutores.'.format(course_author.get_name));
                    });
                }
            };

            $scope.save_all_instructors = function() {
                var promises_list = [];
                $scope.courseProfessors.forEach(function(course_author) {
                    delete course_author.picture;
                    course_author.$update();
                    promises_list.push(course_author.$promise);
                });
                $q.all([promises_list]).then(function() {
                        $scope.alert.success('Posição dos instrutores salva com sucesso.');
                    }, function() {
                        $scope.alert.error('Não foi possível salvar a posição dos instrutores!');
                    }
                );
            };

            $scope.open_professor_modal = function(course_professor) {

                $scope.courseProfessors_original = angular.copy($scope.courseProfessors);
                var modalInstance = $uibModal.open({
                       templateUrl: 'course_professor_modal.html',
                       controller: CourseProfessorModalInstanceCtrl,
                       resolve: {
                           course_professor: function () {
                               return course_professor;
                           }
                       }
                });

                modalInstance.result.then(function (course_professor) {
                    var course_professor_picture_file = course_professor.course_professor_picture_file;
                    var fu = new FormUpload();
                    fu.addField('picture', course_professor_picture_file);

                    if (course_professor.picture !== null)
                        delete course_professor.picture;

                    if (course_professor.id === undefined){
                        course_professor.course = $scope.course_id;
                        course_professor.position = $scope.courseProfessors.length;

                        course_professor.$save({}, function (course_professor){

                            if (course_professor_picture_file){
                                // return a new promise that file will be uploaded
                                fu.sendTo('/api/course_professor_picture/' + course_professor.id)
                                    .then(function(response){
                                        course_professor.get_picture_url = '/media/' + response.data.picture;
                                        course_professor.picture = '/media/' + response.data.picture;
                                        //$scope.alert.success('A imagem atualizada.');
                                });
                            }

                            $scope.courseProfessors.push(course_professor);
                            $scope.alert.success('Professor do curso atualizado com sucesso!');
                        }, function (){
                            $scope.alert.error('Não foi possível atualizar o professor do curso!');
                        });
                    } else {
                        course_professor.$update({id: course_professor.id}, function (){
                            if (course_professor_picture_file){
                                // return a new promise that file will be uploaded
                                fu.sendTo('/api/course_professor_picture/' + course_professor.id)
                                    .then(function(response){
                                        course_professor.get_picture_url = '/media/' + response.data.picture;
                                        course_professor.picture = '/media/' + response.data.picture;
                                });
                            }
                            $scope.alert.success('Professor do curso atualizado com sucesso!');
                        }, function (){
                            $scope.alert.error('Não foi possível atualizar o professor do curso!');
                        });
                    }
                },
                function() {
                    $scope.courseProfessors = angular.copy($scope.courseProfessors_original);
                });
            };

            var CourseProfessorModalInstanceCtrl = function($scope, $uibModalInstance, course_professor) {
                if (course_professor === undefined) {
                    course_professor = new CourseProfessor();
                    $scope.linked_with_user = null;
                }

                $scope.course_professor = course_professor;

                $scope.link_user_on = function () {
                    $scope.linked_with_user = true;
                    if (course_professor.user) {
                        if (!course_professor.name && course_professor.user_info.name) {
                            $scope.name_from_user_profile = true;
                        }

                        if (!course_professor.biography && course_professor.user_info.biography) {
                            $scope.biography_from_user_profile = true;
                        }

                        if (!course_professor.picture && course_professor.user_info.picture) {
                            $scope.picture_from_user_profile = true;
                        }
                    }
                };

                if (course_professor.user_info) {
                    $scope.link_user_on();
                } else if ($scope.course_professor.id) {
                    $scope.linked_with_user = false;
                }

                $scope.remove_user_link = function () {
                    $scope.linked_with_user = false;
                    $scope.name_from_user_profile = false;
                    $scope.biography_from_user_profile = false;
                    $scope.picture_from_user_profile = false;
                    $scope.show_form = true;
                };

                $scope.cancel = function () {
                    $uibModalInstance.dismiss();
                };

                $scope.on_select_professor = function(model) {
                    $scope.course_professor.user = model.id;
                    $scope.course_professor.user_info = model;
                    $scope.link_user_on();
                    $scope.show_form = true;
                };

                $scope.remove_professor = function() {
                    $scope.course_professor.user = null;
                    $scope.course_professor.user_info = null;
                    $scope.name_from_user_profile = false;
                    $scope.biography_from_user_profile = false;
                    $scope.picture_from_user_profile = false;
                };

                $scope.save_course_professors = function() {

                    if ($scope.linked_with_user) {
                        if ($scope.name_from_user_profile) {
                            $scope.course_professor.name = null;
                        }
                        if ($scope.biography_from_user_profile) {
                            $scope.course_professor.biography = null;
                        }
                        if ($scope.picture_from_user_profile) {
                            $scope.course_professor.picture = null;
                            $scope.course_professor.get_picture_url = null;
                        }
                    } else {
                        if ($scope.course_professor.user) {
                            delete $scope.course_professor.user;
                        }
                    }

                    if ($scope.course_professor_picture_file && !$scope.picture_from_user_profile) {
                        $scope.course_professor.course_professor_picture_file = $scope.course_professor_picture_file;
                    }
                    $uibModalInstance.close($scope.course_professor);
                    $scope.course_professor = undefined;
                };

                $scope.getUsers = function(val) {
                    return $http.get('/api/user_search', {
                        params: {
                          name: val,
                          sensor: false
                        }
                    }).then(function(res){
                        var professors_found = [];
                        angular.forEach(res.data, function(item){
                            var formated_name = '';
                            if (item.first_name)
                                formated_name += item.first_name;
                            if (item.last_name)
                                formated_name = formated_name + ' ' + item.last_name;
                            if (formated_name)
                                formated_name = formated_name + ' - ';
                            formated_name += item.username;
                            if (item.email)
                                formated_name = formated_name + ' - ' + item.email;
                            item.formated_name = formated_name;
                            professors_found.push(item);
                        });
                        return professors_found;
                    });
                };
            };

            $scope.saveLesson = function(lesson) {
                return lesson.saveOrUpdate()
                    .then(function(){
                        $scope.alert.success('Lição atualizada com sucesso');
                    })['catch'](function(){
                        $scope.alert.error('Não foi possível salvar a lição');
                    });
            };

            $scope.deleteLesson = function(lesson) {
                var msg = 'Tem certeza que deseja remover a lição "{0}"'.format(lesson.name);
                if(!window.confirm(msg)) return;

                return lesson.$delete().then(function(){
                    var filter = function(l) { return l.id !== lesson.id; };
                    $scope.lessons = $scope.lessons.filter(filter);
                    $scope.alert.success('"{0}" foi removido.'.format(lesson.name));
                });
            };

            $scope.repositionLessons = function() {
                $scope.lessons.forEach(function(lesson, i){
                    lesson.position = i;
                });
            };

            $scope.repositionInstructors = function() {
                $scope.courseProfessors.forEach(function(p, i){
                    p.position = i;
                });
            };

            $scope.saveAllLessons = function() {
                var i = 0;
                function __saveLessons() {
                    if(i < $scope.lessons.length) {
                        return $scope.lessons[i++]
                                     .saveOrUpdate()
                                     .then(__saveLessons);
                    }
                }

                $scope.alert.warn('Atualizando aulas');

                __saveLessons()
                    .then(function(){
                        $scope.alert.success('As aulas foram atualizadas');
                    })['catch'](function(){
                        $scope.alert.error('Algum problema impediu a atualização das aulas');
                    });
            };

        }
    ]);

})(window.angular);
<commit_msg>Prevent error during course save if there were no video_intro before<commit_after>(function(angular){

    var app = angular.module('new-course');

    app.controller('CourseEditController',
        ['$scope', '$window', '$uibModal', '$http', '$q', 'Course',  'CourseAuthor', 'Lesson', '$filter', 'youtubePlayerApi', 'VideoData', 'FormUpload', 'uibDateParser',
        function($scope, $window, $uibModal, $http , $q, Course,  CourseProfessor, Lesson, $filter, youtubePlayerApi, VideoData, FormUpload, uibDateParser) {

            $scope.errors = {};
            var httpErrors = {
                '400': 'Os campos não foram preenchidos corretamente.',
                '403': 'Você não tem permissão para ver conteúdo nesta página.',
                '404': 'Este curso não existe!'
            };

            $scope.course_id = parseInt($window.course_id, 10);

            $scope.courseProfessors = [];
            $scope.lessons = [];

            Course.get({id: $scope.course_id}, function(course){
                if(course.intro_video) {
                    youtubePlayerApi.videoId = course.intro_video.youtube_id;
                }
                document.title = 'Curso: {0}'.format(course.name);
                $scope.addThumb = !course.thumbnail_url;
                $scope.addHomeThumb = !course.home_thumbnail_url;
                course.start_date = uibDateParser.parse(course.start_date, "yyyy-MM-dd");
                $scope.course = course;
            });

            $scope.lessons = Lesson.query({
              'course__id': $scope.course_id
            });

            $scope.courseProfessors = CourseProfessor.query({
              course: $scope.course_id
            });

            var player;
            $scope.playerReady = false;
            youtubePlayerApi.loadPlayer().then(function(p){
                player = p;
                $scope.playerReady = true;
            });

            $scope.$watch('course.intro_video.youtube_id', function(vid, oldVid){
                if(!vid || vid === oldVid) return;
                if(player) player.cueVideoById(vid);
                VideoData.load(vid).then(function(data){
                    if (data.items.length > 0)
                        $scope.course.intro_video.name = data.items[0].snippet.title;
                });
            });

            $scope.setCurrentUnitVideo = function() {
                //
                // support pasting both long and short urls from youtube
                // eg. http://youtu.be/8uj7YSqby7s
                //
                var complete_url = /^.*(youtu\.be\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=)([^#\&\?]*).*/;
                var result = complete_url.exec($scope.youtube_id);
                if (result && result[2].length == 11) {
                    // If this is the first time a intro_video is set, prepare the variable
                    if($scope.course.intro_video === 'undefined')
                        $scope.course.intro_video = {};
                    $scope.course.intro_video.youtube_id = result[2];
                }
            };

            function showFieldErrors(response) {
                $scope.errors = response.data;
                var messages = [];
                for(var att in response.data) {
                    var message = response.data[att];
                    if(Course.fields && Course.fields[att]) {
                        message = Course.fields[att].label + ': ' + message;
                    }
                    messages.push(message);
                }
                $scope.alert.error('Encontramos alguns erros!', messages, true);
            }

            $scope.saveThumb = function() {
                if(! $scope.thumbfile) {
                    return;
                }

                if ($scope.course.id) {
                    var fu = new FormUpload();
                    fu.addField('thumbnail', $scope.thumbfile);
                    // return a new promise that file will be uploaded
                    return fu.sendTo('/api/coursethumbs/' + $scope.course.id)
                        .then(function(){
                            $scope.alert.success('A imagem atualizada.');
                        });
                }
            };

            $scope.saveHomeThumb = function() {
                if(! $scope.home_thumbfile) {
                    return;
                }

                if ($scope.course.id) {
                    var fu = new FormUpload();
                    fu.addField('home_thumbnail', $scope.home_thumbfile);
                    // return a new promise that file will be uploaded
                    return fu.sendTo('/api/coursethumbs/' + $scope.course.id)
                        .then(function(){
                            $scope.alert.success('A imagem atualizada.');
                        });
                }
            };

            $scope.export_course = function() {
                $window.open('/admin/course/' + $scope.course_id + '/export/', '_blank', '');
            };

            $scope.saveCourse = function() {
                if(!$scope.course.hasVideo()){
                    delete $scope.course.intro_video;
                }
                if(!$scope.course.slug){
                    $scope.course.slug = $filter('slugify')($scope.course.name);
                }

                if ($scope.course.start_date) {
                    $scope.course.start_date = $filter('date')($scope.course.start_date, 'yyyy-MM-dd');
                }

                $scope.course.save()
                    .then(function(course){
                        course.start_date = uibDateParser.parse(course.start_date, "yyyy-MM-dd");
                        return $scope.saveThumb();
                    })
                    .then(function(){
                        $scope.alert.success('Alterações salvas com sucesso!');
                    })['catch'](showFieldErrors);
            };

            $scope.publishCourse = function() {
                $scope.course.status = 'published';
                $scope.saveCourse();
            };

            $scope.set_course_as_draft = function() {
                if (confirm('Ao tornar o curso um rascunho, ele não será mais visível para os usuários! Tem certeza de que quer continuar?')){
                    $scope.course.status = 'draft';
                    $scope.saveCourse();
                }
            };

            $scope.deleteCourse = function() {
                if(!confirm('Tem certeza que deseja remover este curso?')) return;

                $scope.course.$delete()
                    .then(function(){
                        document.location.href = '/admin/courses';
                    });
            };

            $scope.delete_instructor = function(course_author) {

                var confirm_exclude_instructor_msg = 'Tem certeza que deseja remover "{0}" da lista de instrutores deste curso?'.format(course_author.get_name);
                if (window.confirm(confirm_exclude_instructor_msg)){
                    course_author.$delete().then(function(){
                        var index = $scope.courseProfessors.indexOf(course_author);
                        $scope.courseProfessors.splice(index, 1);
                        $scope.alert.success('"{0}" foi removido da lista de instrutores.'.format(course_author.get_name));
                    }, function(){
                        $scope.alert.error('Erro ao remover "{0}" da lista de instrutores.'.format(course_author.get_name));
                    });
                }
            };

            $scope.save_all_instructors = function() {
                var promises_list = [];
                $scope.courseProfessors.forEach(function(course_author) {
                    delete course_author.picture;
                    course_author.$update();
                    promises_list.push(course_author.$promise);
                });
                $q.all([promises_list]).then(function() {
                        $scope.alert.success('Posição dos instrutores salva com sucesso.');
                    }, function() {
                        $scope.alert.error('Não foi possível salvar a posição dos instrutores!');
                    }
                );
            };

            $scope.open_professor_modal = function(course_professor) {

                $scope.courseProfessors_original = angular.copy($scope.courseProfessors);
                var modalInstance = $uibModal.open({
                       templateUrl: 'course_professor_modal.html',
                       controller: CourseProfessorModalInstanceCtrl,
                       resolve: {
                           course_professor: function () {
                               return course_professor;
                           }
                       }
                });

                modalInstance.result.then(function (course_professor) {
                    var course_professor_picture_file = course_professor.course_professor_picture_file;
                    var fu = new FormUpload();
                    fu.addField('picture', course_professor_picture_file);

                    if (course_professor.picture !== null)
                        delete course_professor.picture;

                    if (course_professor.id === undefined){
                        course_professor.course = $scope.course_id;
                        course_professor.position = $scope.courseProfessors.length;

                        course_professor.$save({}, function (course_professor){

                            if (course_professor_picture_file){
                                // return a new promise that file will be uploaded
                                fu.sendTo('/api/course_professor_picture/' + course_professor.id)
                                    .then(function(response){
                                        course_professor.get_picture_url = '/media/' + response.data.picture;
                                        course_professor.picture = '/media/' + response.data.picture;
                                        //$scope.alert.success('A imagem atualizada.');
                                });
                            }

                            $scope.courseProfessors.push(course_professor);
                            $scope.alert.success('Professor do curso atualizado com sucesso!');
                        }, function (){
                            $scope.alert.error('Não foi possível atualizar o professor do curso!');
                        });
                    } else {
                        course_professor.$update({id: course_professor.id}, function (){
                            if (course_professor_picture_file){
                                // return a new promise that file will be uploaded
                                fu.sendTo('/api/course_professor_picture/' + course_professor.id)
                                    .then(function(response){
                                        course_professor.get_picture_url = '/media/' + response.data.picture;
                                        course_professor.picture = '/media/' + response.data.picture;
                                });
                            }
                            $scope.alert.success('Professor do curso atualizado com sucesso!');
                        }, function (){
                            $scope.alert.error('Não foi possível atualizar o professor do curso!');
                        });
                    }
                },
                function() {
                    $scope.courseProfessors = angular.copy($scope.courseProfessors_original);
                });
            };

            var CourseProfessorModalInstanceCtrl = function($scope, $uibModalInstance, course_professor) {
                if (course_professor === undefined) {
                    course_professor = new CourseProfessor();
                    $scope.linked_with_user = null;
                }

                $scope.course_professor = course_professor;

                $scope.link_user_on = function () {
                    $scope.linked_with_user = true;
                    if (course_professor.user) {
                        if (!course_professor.name && course_professor.user_info.name) {
                            $scope.name_from_user_profile = true;
                        }

                        if (!course_professor.biography && course_professor.user_info.biography) {
                            $scope.biography_from_user_profile = true;
                        }

                        if (!course_professor.picture && course_professor.user_info.picture) {
                            $scope.picture_from_user_profile = true;
                        }
                    }
                };

                if (course_professor.user_info) {
                    $scope.link_user_on();
                } else if ($scope.course_professor.id) {
                    $scope.linked_with_user = false;
                }

                $scope.remove_user_link = function () {
                    $scope.linked_with_user = false;
                    $scope.name_from_user_profile = false;
                    $scope.biography_from_user_profile = false;
                    $scope.picture_from_user_profile = false;
                    $scope.show_form = true;
                };

                $scope.cancel = function () {
                    $uibModalInstance.dismiss();
                };

                $scope.on_select_professor = function(model) {
                    $scope.course_professor.user = model.id;
                    $scope.course_professor.user_info = model;
                    $scope.link_user_on();
                    $scope.show_form = true;
                };

                $scope.remove_professor = function() {
                    $scope.course_professor.user = null;
                    $scope.course_professor.user_info = null;
                    $scope.name_from_user_profile = false;
                    $scope.biography_from_user_profile = false;
                    $scope.picture_from_user_profile = false;
                };

                $scope.save_course_professors = function() {

                    if ($scope.linked_with_user) {
                        if ($scope.name_from_user_profile) {
                            $scope.course_professor.name = null;
                        }
                        if ($scope.biography_from_user_profile) {
                            $scope.course_professor.biography = null;
                        }
                        if ($scope.picture_from_user_profile) {
                            $scope.course_professor.picture = null;
                            $scope.course_professor.get_picture_url = null;
                        }
                    } else {
                        if ($scope.course_professor.user) {
                            delete $scope.course_professor.user;
                        }
                    }

                    if ($scope.course_professor_picture_file && !$scope.picture_from_user_profile) {
                        $scope.course_professor.course_professor_picture_file = $scope.course_professor_picture_file;
                    }
                    $uibModalInstance.close($scope.course_professor);
                    $scope.course_professor = undefined;
                };

                $scope.getUsers = function(val) {
                    return $http.get('/api/user_search', {
                        params: {
                          name: val,
                          sensor: false
                        }
                    }).then(function(res){
                        var professors_found = [];
                        angular.forEach(res.data, function(item){
                            var formated_name = '';
                            if (item.first_name)
                                formated_name += item.first_name;
                            if (item.last_name)
                                formated_name = formated_name + ' ' + item.last_name;
                            if (formated_name)
                                formated_name = formated_name + ' - ';
                            formated_name += item.username;
                            if (item.email)
                                formated_name = formated_name + ' - ' + item.email;
                            item.formated_name = formated_name;
                            professors_found.push(item);
                        });
                        return professors_found;
                    });
                };
            };

            $scope.saveLesson = function(lesson) {
                return lesson.saveOrUpdate()
                    .then(function(){
                        $scope.alert.success('Lição atualizada com sucesso');
                    })['catch'](function(){
                        $scope.alert.error('Não foi possível salvar a lição');
                    });
            };

            $scope.deleteLesson = function(lesson) {
                var msg = 'Tem certeza que deseja remover a lição "{0}"'.format(lesson.name);
                if(!window.confirm(msg)) return;

                return lesson.$delete().then(function(){
                    var filter = function(l) { return l.id !== lesson.id; };
                    $scope.lessons = $scope.lessons.filter(filter);
                    $scope.alert.success('"{0}" foi removido.'.format(lesson.name));
                });
            };

            $scope.repositionLessons = function() {
                $scope.lessons.forEach(function(lesson, i){
                    lesson.position = i;
                });
            };

            $scope.repositionInstructors = function() {
                $scope.courseProfessors.forEach(function(p, i){
                    p.position = i;
                });
            };

            $scope.saveAllLessons = function() {
                var i = 0;
                function __saveLessons() {
                    if(i < $scope.lessons.length) {
                        return $scope.lessons[i++]
                                     .saveOrUpdate()
                                     .then(__saveLessons);
                    }
                }

                $scope.alert.warn('Atualizando aulas');

                __saveLessons()
                    .then(function(){
                        $scope.alert.success('As aulas foram atualizadas');
                    })['catch'](function(){
                        $scope.alert.error('Algum problema impediu a atualização das aulas');
                    });
            };

        }
    ]);

})(window.angular);
<|endoftext|>


<commit_before>/**
 * Main App Controller for the Angular Material Starter App
 * @param UsersDataService
 * @param $mdSidenav
 * @constructor
 */
function EthereumService($q) {

    var self = this;

    self.abi = [ { "constant": true, "inputs": [], "name": "getRequestCount", "outputs": [ { "name": "", "type": "uint256", "value": "0" } ], "payable": false, "type": "function" }, { "constant": true, "inputs": [ { "name": "serial", "type": "string" } ], "name": "getCustomer", "outputs": [ { "name": "", "type": "address", "value": "0x0000000000000000000000000000000000000000" } ], "payable": false, "type": "function" }, { "constant": true, "inputs": [ { "name": "id", "type": "uint256" } ], "name": "getClaim", "outputs": [ { "name": "", "type": "address", "value": "0x0000000000000000000000000000000000000000" }, { "name": "", "type": "uint256", "value": "0" }, { "name": "", "type": "string", "value": "" }, { "name": "", "type": "uint256", "value": "0" } ], "payable": false, "type": "function" }, { "constant": false, "inputs": [ { "name": "serial", "type": "string" }, { "name": "amount", "type": "uint256" } ], "name": "claimWaranty", "outputs": [], "payable": false, "type": "function" }, { "constant": false, "inputs": [ { "name": "contractAddress", "type": "address" } ], "name": "updateCalculator", "outputs": [], "payable": false, "type": "function" }, { "constant": true, "inputs": [], "name": "getClaimCount", "outputs": [ { "name": "", "type": "uint256", "value": "0" } ], "payable": false, "type": "function" }, { "constant": true, "inputs": [ { "name": "serial", "type": "string" } ], "name": "getEndDate", "outputs": [ { "name": "", "type": "uint256", "value": "0" } ], "payable": false, "type": "function" }, { "constant": true, "inputs": [ { "name": "id", "type": "uint256" } ], "name": "getRequest", "outputs": [ { "name": "", "type": "uint256", "value": "0" }, { "name": "", "type": "string", "value": "" }, { "name": "", "type": "string", "value": "" }, { "name": "", "type": "string", "value": "" }, { "name": "", "type": "address", "value": "0x0000000000000000000000000000000000000000" }, { "name": "", "type": "string", "value": "" }, { "name": "", "type": "uint256", "value": "0" }, { "name": "", "type": "uint256", "value": "0" } ], "payable": false, "type": "function" }, { "constant": true, "inputs": [ { "name": "serial", "type": "string" } ], "name": "isWarrantyValid", "outputs": [ { "name": "", "type": "bool", "value": true } ], "payable": false, "type": "function" }, { "constant": false, "inputs": [ { "name": "serial", "type": "string" }, { "name": "customer", "type": "address" }, { "name": "endDate", "type": "uint256" }, { "name": "price", "type": "uint256" } ], "name": "requestWarranty", "outputs": [], "payable": false, "type": "function" }, { "inputs": [], "type": "constructor" } ];
    self.dappId = "epam.hackathon";

    self.contractAddress = "0x64910c65a13B08634D7263b5f06c0C67AfFf36Cf";

    self.web3 = web3;
    self.WarrantyContract = web3.eth.contract(self.abi).at(self.contractAddress);

    self.getInfo = function (serial) {
        return $q.all({
            isWarrantyValid: self.isWarrantyValid(serial),
            customer: self.getCustomer(serial),
            warrantyEndDate: self.getWarrantyEndDate(serial)
        });

    }

    self.getReportData = function (month) {
        return $q.all({
            requests: self.getRequestCount(),
            claims: self.getClaimCount()
        }).then(function (counts) {
            var getClaims = [];
            for (var i = 0; i < counts.claims; i++) {
                getClaims.push(self.WarrantyContract.getClaim(idx, function (err, result) {
                        if (err) {
                            defer.reject(err);
                        } else {
                            defer.resolve(result);
                        }
                    })
                )
            }
            var getRequests = [];
            for (var i = 0; i < counts.requests; i++) {
                getClaims.push(self.WarrantyContract.getRequest(idx, function (err, result) {
                        if (err) {
                            defer.reject(err);
                        } else {
                            defer.resolve(result);
                        }
                    })
                )
            }
            return $q.all({
                claims: getClaims,
                requests: getRequests
            });
        }).then(function(results){
            var report = "productType,productName,serial,pricePaid,claimDate,claimAmount,claimantId\n";
            results.claims.forEach(function (claim) {
                report += "," //productType
                report += "," //productName
                report += claim.serial + "," //productSerial
            });
            return $q(function (resolve, reject) {
                resolve(report);
            });
        });

    }

    self.isWarrantyValid = function (serial) {
        console.log('is warranty valid');
        var defer = $q.defer();

        self.WarrantyContract.isWarrantyValid(serial, function (err, result) {
            if (err) {
                defer.reject(err);
            } else {
                defer.resolve(result);
            }
        });

        return defer.promise;
    }

    self.getCustomer = function (serial) {
        var defer = $q.defer();

        self.WarrantyContract.getCustomer(serial, function (err, result) {
            if (err) {
                defer.reject(err);
            } else {
                defer.resolve(result);
            }
        });

        return defer.promise;
    }

    self.getRequestCount = function () {
        var defer = $q.defer();

        self.WarrantyContract.getRequestCount(function (err, result) {
            if (err) {
                defer.reject(err);
            } else {
                defer.resolve(result);
            }
        });

        return defer.promise;
    }

    self.getClaimCount = function () {
        var defer = $q.defer();

        self.WarrantyContract.getClaimCount(function (err, result) {
            if (err) {
                defer.reject(err);
            } else {
                defer.resolve(result);
            }
        });

        return defer.promise;
    }

    self.getRequest = function (idx) {
        var defer = $q.defer();

        self.WarrantyContract.getRequest(idx, function (err, result) {
            if (err) {
                defer.reject(err);
            } else {
                defer.resolve(result);
            }
        });

        return defer.promise;
    }

    self.getClaim = function (idx) {
        var defer = $q.defer();

        self.WarrantyContract.getClaim(idx, function (err, result) {
            if (err) {
                defer.reject(err);
            } else {
                defer.resolve(result);
            }
        });

        return defer.promise;
    }

    self.getWarrantyEndDate = function (serial) {
        var defer = $q.defer();

        self.WarrantyContract.getEndDate(serial, function (err, result) {
            if (err) {
                defer.reject(err);
            } else {
                defer.resolve(result);
            }
        });

        return defer.promise;
    }

    self.requestWarranty = function (serial, owner, endDate) {
        var defer = $q.defer();
        self.WarrantyContract.requestWarranty(serial, owner, endDate.getTime(), function (err, result) {
            if (err) {
                defer.reject(err);
            } else {
                defer.resolve(result);
            }
        });

        return defer.promise;
    }

    return self;
}

export default ['$q', EthereumService];
<commit_msg>clean commented out code<commit_after>/**
 * Main App Controller for the Angular Material Starter App
 * @param UsersDataService
 * @param $mdSidenav
 * @constructor
 */
function EthereumService($q) {

    var self = this;

    self.abi = [ { "constant": true, "inputs": [], "name": "getRequestCount", "outputs": [ { "name": "", "type": "uint256", "value": "0" } ], "payable": false, "type": "function" }, { "constant": true, "inputs": [ { "name": "serial", "type": "string" } ], "name": "getCustomer", "outputs": [ { "name": "", "type": "address", "value": "0x0000000000000000000000000000000000000000" } ], "payable": false, "type": "function" }, { "constant": true, "inputs": [ { "name": "id", "type": "uint256" } ], "name": "getClaim", "outputs": [ { "name": "", "type": "address", "value": "0x0000000000000000000000000000000000000000" }, { "name": "", "type": "uint256", "value": "0" }, { "name": "", "type": "string", "value": "" }, { "name": "", "type": "uint256", "value": "0" } ], "payable": false, "type": "function" }, { "constant": false, "inputs": [ { "name": "serial", "type": "string" }, { "name": "amount", "type": "uint256" } ], "name": "claimWaranty", "outputs": [], "payable": false, "type": "function" }, { "constant": false, "inputs": [ { "name": "contractAddress", "type": "address" } ], "name": "updateCalculator", "outputs": [], "payable": false, "type": "function" }, { "constant": true, "inputs": [], "name": "getClaimCount", "outputs": [ { "name": "", "type": "uint256", "value": "0" } ], "payable": false, "type": "function" }, { "constant": true, "inputs": [ { "name": "serial", "type": "string" } ], "name": "getEndDate", "outputs": [ { "name": "", "type": "uint256", "value": "0" } ], "payable": false, "type": "function" }, { "constant": true, "inputs": [ { "name": "id", "type": "uint256" } ], "name": "getRequest", "outputs": [ { "name": "", "type": "uint256", "value": "0" }, { "name": "", "type": "string", "value": "" }, { "name": "", "type": "string", "value": "" }, { "name": "", "type": "string", "value": "" }, { "name": "", "type": "address", "value": "0x0000000000000000000000000000000000000000" }, { "name": "", "type": "string", "value": "" }, { "name": "", "type": "uint256", "value": "0" }, { "name": "", "type": "uint256", "value": "0" } ], "payable": false, "type": "function" }, { "constant": true, "inputs": [ { "name": "serial", "type": "string" } ], "name": "isWarrantyValid", "outputs": [ { "name": "", "type": "bool", "value": true } ], "payable": false, "type": "function" }, { "constant": false, "inputs": [ { "name": "serial", "type": "string" }, { "name": "customer", "type": "address" }, { "name": "endDate", "type": "uint256" }, { "name": "price", "type": "uint256" } ], "name": "requestWarranty", "outputs": [], "payable": false, "type": "function" }, { "inputs": [], "type": "constructor" } ];
    self.dappId = "epam.hackathon";

    self.contractAddress = "0x64910c65a13B08634D7263b5f06c0C67AfFf36Cf";

    self.web3 = web3;
    self.WarrantyContract = web3.eth.contract(self.abi).at(self.contractAddress);

    self.getInfo = function (serial) {
        return $q.all({
            isWarrantyValid: self.isWarrantyValid(serial),
            customer: self.getCustomer(serial),
            warrantyEndDate: self.getWarrantyEndDate(serial)
        });

    }

    self.getReportData = function (month) {
        return $q.all({
            requests: self.getRequestCount(),
            claims: self.getClaimCount()
        }).then(function (counts) {
            var getClaims = [];
            for (var i = 0; i < counts.claims; i++) {
                getClaims.push(self.WarrantyContract.getClaim(idx, function (err, result) {
                        if (err) {
                            defer.reject(err);
                        } else {
                            defer.resolve(result);
                        }
                    })
                )
            }
            var getRequests = [];
            for (var i = 0; i < counts.requests; i++) {
                getClaims.push(self.WarrantyContract.getRequest(idx, function (err, result) {
                        if (err) {
                            defer.reject(err);
                        } else {
                            defer.resolve(result);
                        }
                    })
                )
            }
            return $q.all({
                claims: getClaims,
                requests: getRequests
            });
        }).then(function(results){
            var report = "productType,productName,serial,pricePaid,claimDate,claimAmount,claimantId\n";
            results.claims.forEach(function (claim) {
                report += "," //productType
                report += "," //productName
                report += claim.serial + "," //productSerial
            });
            return $q(function (resolve, reject) {
                resolve(report);
            });
        });

    }

    self.isWarrantyValid = function (serial) {
        console.log('is warranty valid');
        var defer = $q.defer();

        self.WarrantyContract.isWarrantyValid(serial, function (err, result) {
            if (err) {
                defer.reject(err);
            } else {
                defer.resolve(result);
            }
        });

        return defer.promise;
    }

    self.getCustomer = function (serial) {
        var defer = $q.defer();

        self.WarrantyContract.getCustomer(serial, function (err, result) {
            if (err) {
                defer.reject(err);
            } else {
                defer.resolve(result);
            }
        });

        return defer.promise;
    }

    self.getRequestCount = function () {
        var defer = $q.defer();

        self.WarrantyContract.getRequestCount(function (err, result) {
            if (err) {
                defer.reject(err);
            } else {
                defer.resolve(result);
            }
        });

        return defer.promise;
    }

    self.getClaimCount = function () {
        var defer = $q.defer();

        self.WarrantyContract.getClaimCount(function (err, result) {
            if (err) {
                defer.reject(err);
            } else {
                defer.resolve(result);
            }
        });

        return defer.promise;
    }

    self.getRequest = function (idx) {
        var defer = $q.defer();

        self.WarrantyContract.getRequest(idx, function (err, result) {
            if (err) {
                defer.reject(err);
            } else {
                defer.resolve(result);
            }
        });

        return defer.promise;
    }

    self.getClaim = function (idx) {
        var defer = $q.defer();

        self.WarrantyContract.getClaim(idx, function (err, result) {
            if (err) {
                defer.reject(err);
            } else {
                defer.resolve(result);
            }
        });

        return defer.promise;
    }

    self.getWarrantyEndDate = function (serial) {
        var defer = $q.defer();

        self.WarrantyContract.getEndDate(serial, function (err, result) {
            if (err) {
                defer.reject(err);
            } else {
                defer.resolve(result);
            }
        });

        return defer.promise;
    }

    self.requestWarranty = function (serial, owner, endDate) {
        var defer = $q.defer();
        self.WarrantyContract.requestWarranty(serial, owner, endDate.getTime() / 1000, function (err, result) {
            if (err) {
                defer.reject(err);
            } else {
                defer.resolve(result);
            }
        });

        return defer.promise;
    }

    return self;
}

export default ['$q', EthereumService];
<|endoftext|>


<commit_before>var display = require('display');

var options = {};

options.coffee = function () {
  return 'Coffee';
};

options.coffeeWithSugar = function () {
  return 'Coffee with sugar';
};

options.coffeeWithCream = function () {
  return 'Coffee with cream';
};

options.coffeeWithCreamAndSugar = function () {
  return 'Coffee with cream and sugar';
};

options.soup = function () {
  return 'Soup';
};<commit_msg>Update menu<commit_after>var display = require('display');

var options = {};

options.coffee = function () {
  return 'Coffee';
};

options.coffeeWithSugar = function () {
  return 'Coffee with sugar';
};

options.coffeeWithCream = function () {
  return 'Coffee with cream';
};

options.coffeeWithCreamAndSugar = function () {
  return 'Coffee with cream and sugar';
};

options.soup = function () {
  return 'Soup';
};

module.exports = menu;<|endoftext|>


<commit_before>import PropTypes from "prop-types";
import React, { useEffect } from "react";
import { usePrevious } from "react-use";
import styled from "styled-components";

import style from "@agir/front/genericComponents/_variables.scss";

import background from "@agir/front/genericComponents/images/illustration-404.svg";
import { useIsOffline } from "@agir/front/offline/hooks";

import TopBar from "@agir/front/allPages/TopBar";
import Button from "@agir/front/genericComponents/Button";
import Spacer from "@agir/front/genericComponents/Spacer";

import illustration from "./illustration.svg";

const Container = styled.div`
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  width: 100%;
  min-height: calc(100vh - 74px);
  position: relative;
  overflow: auto;
  background-image: url("${background}");
  background-position: center;
  background-size: cover;
  background-repeat: no-repeat;
  padding: 28px 14px;
`;

const InterrogationMark = styled.div`
  width: 56px;
  height: 56px;
  border-radius: 56px;
  font-size: 30px;
  background-color: ${style.secondary500};
  display: inline-flex;
  justify-content: center;
  align-items: center;
  font-weight: 500;
`;

const StyledButton = styled(Button)`
  max-width: 450px;
  width: 100%;
  margin-top: 2rem;
  justify-content: center;
`;

const PageStyle = styled.div`
  display: flex;
  flex-direction: column;
  justify-content: center;
`;

const OfflineBlock = styled.div`
  & > * {
    margin: 0 auto;
    padding: 0 16px;
  }

  & > h1 {
    font-size: 20px;
  }
`;

export const NotFoundPage = ({
  isTopBar = true,
  title = "Page",
  subtitle = "Cette page",
  reloadOnReconnection = true,
}) => {
  const isOffline = useIsOffline();
  const wasOffline = usePrevious(isOffline);

  useEffect(() => {
    reloadOnReconnection &&
      wasOffline &&
      !isOffline &&
      window.location.reload();
  }, [reloadOnReconnection, isOffline, wasOffline]);

  if (isOffline === null) return null;

  return (
    <PageStyle>
      {isTopBar && (
        <>
          <TopBar />
          <Spacer size="74px" />
        </>
      )}
      <Container>
        {isOffline ? (
          <OfflineBlock>

Pas de connexion

              Connectez-vous à un

              réseau Wi-Fi ou mobile

          </OfflineBlock>
        ) : (
          <>
            <InterrogationMark>?</InterrogationMark>

              {title + " "}introuvable

            {subtitle + " "}n’existe pas ou plus

              ou consulter le{" "}
              centre d'aide

          </>
        )}
      </Container>
    </PageStyle>
  );
};
NotFoundPage.propTypes = {
  isTopBar: PropTypes.bool,
  title: PropTypes.string,
  subtitle: PropTypes.string,
  reloadOnReconnection: PropTypes.bool,
};
export default NotFoundPage;
<commit_msg>feat: add log for 404<commit_after>import PropTypes from "prop-types";
import React, { useEffect } from "react";
import { usePrevious } from "react-use";
import styled from "styled-components";

import style from "@agir/front/genericComponents/_variables.scss";

import background from "@agir/front/genericComponents/images/illustration-404.svg";
import { useIsOffline } from "@agir/front/offline/hooks";

import TopBar from "@agir/front/allPages/TopBar";
import Button from "@agir/front/genericComponents/Button";
import Spacer from "@agir/front/genericComponents/Spacer";

import illustration from "./illustration.svg";

import generateLogger from "@agir/lib/utils/logger";

const logger = generateLogger(__filename);

const Container = styled.div`
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  width: 100%;
  min-height: calc(100vh - 74px);
  position: relative;
  overflow: auto;
  background-image: url("${background}");
  background-position: center;
  background-size: cover;
  background-repeat: no-repeat;
  padding: 28px 14px;
`;

const InterrogationMark = styled.div`
  width: 56px;
  height: 56px;
  border-radius: 56px;
  font-size: 30px;
  background-color: ${style.secondary500};
  display: inline-flex;
  justify-content: center;
  align-items: center;
  font-weight: 500;
`;

const StyledButton = styled(Button)`
  max-width: 450px;
  width: 100%;
  margin-top: 2rem;
  justify-content: center;
`;

const PageStyle = styled.div`
  display: flex;
  flex-direction: column;
  justify-content: center;
`;

const OfflineBlock = styled.div`
  & > * {
    margin: 0 auto;
    padding: 0 16px;
  }

  & > h1 {
    font-size: 20px;
  }
`;

export const NotFoundPage = ({
  isTopBar = true,
  title = "Page",
  subtitle = "Cette page",
  reloadOnReconnection = true,
}) => {
  const isOffline = useIsOffline();
  const wasOffline = usePrevious(isOffline);

  useEffect(() => {
    reloadOnReconnection &&
      wasOffline &&
      !isOffline &&
      window.location.reload();
  }, [reloadOnReconnection, isOffline, wasOffline]);

  if (isOffline === null) return null;

  if (!isOffline) {
    logger.error(`React 404 on page ${window.location.pathname}`);
  }

  return (
    <PageStyle>
      {isTopBar && (
        <>
          <TopBar />
          <Spacer size="74px" />
        </>
      )}
      <Container>
        {isOffline ? (
          <OfflineBlock>

Pas de connexion

              Connectez-vous à un

              réseau Wi-Fi ou mobile

          </OfflineBlock>
        ) : (
          <>
            <InterrogationMark>?</InterrogationMark>

              {title + " "}introuvable

            {subtitle + " "}n’existe pas ou plus

              ou consulter le{" "}
              centre d'aide

          </>
        )}
      </Container>
    </PageStyle>
  );
};
NotFoundPage.propTypes = {
  isTopBar: PropTypes.bool,
  title: PropTypes.string,
  subtitle: PropTypes.string,
  reloadOnReconnection: PropTypes.bool,
};
export default NotFoundPage;
<|endoftext|>