moved timer.js to public/js
diff --git a/erpnext/public/js/projects/timer.js b/erpnext/public/js/projects/timer.js
new file mode 100644
index 0000000..1b75b94
--- /dev/null
+++ b/erpnext/public/js/projects/timer.js
@@ -0,0 +1,140 @@
+frappe.provide("erpnext.timesheet")
+
+erpnext.timesheet.timer = function(frm, disabled, row, timestamp=0) {
+	let dialog = new frappe.ui.Dialog({
+		title: __("Timer"),
+		fields:
+		[
+			{"fieldtype": "Link", "label": __("Activity Type"), "fieldname": "activity_type",
+				"reqd": 1, "options": "Activity Type", "read_only": disabled},
+			{"fieldtype": "Link", "label": __("Project"), "fieldname": "project", "read_only": disabled},
+			{"fieldtype": "Float", "label": __("Expected Hrs"), "fieldname": "expected_hours"},
+			{"fieldtype": "Section Break"},
+			{"fieldtype": "HTML", "fieldname": "timer_html"}
+		]
+		
+	});
+	if (row) {
+		dialog.set_values({
+			'activity_type': row.activity_type,
+			'project': row.project,
+			'expected_hours': row.expected_hours
+		});
+	}
+	dialog.get_field("timer_html").$wrapper.append(frappe.render_template("timesheet"));
+	control_timer(frm, dialog, row, timestamp);
+	dialog.show();
+}
+
+var control_timer = function(frm, dialog, row, timestamp=0) {
+	var $btn_start = $(".playpause .btn-start");
+	var interval = null;
+	var currentIncrement = timestamp
+	var isPaused = false;
+	var initialised = row ? true : false;
+	var clicked = false;
+	var paused_time = 0;
+	// If row with not completed status, initialize timer with the time elapsed on click of 'Start Timer'.
+	if (row) {
+		initialised = true;
+		$btn_start.attr("disabled", true);
+		initialiseTimer();
+	}
+
+	$btn_start.click(function(e) {
+		if (!initialised) {
+			// New activity if no activities found
+			var args = dialog.get_values();
+			if(!args) return;
+			if (!frm.doc.time_logs[0].activity_type) {
+				frm.doc.time_logs = [];
+			}
+			row = frappe.model.add_child(frm.doc, "Timesheet Detail", "time_logs");
+			row.activity_type = args.activity_type;
+			row.from_time = frappe.datetime.get_datetime_as_string();
+			row.expected_hours = args.expected_hours;
+			row.completed = 0;
+			let d = moment(row.from_time)
+			if(row.expected_hours) {
+				d.add(row.expected_hours, "hours");
+				row.to_time = d.format(moment.defaultDatetimeFormat);
+			}
+			frm.refresh_field("time_logs");
+		}
+		if (clicked) {
+			e.preventDefault();
+			return false;
+		}
+
+		if (!initialised) {
+			initialised = true;
+			isPaused = false;
+			$btn_start.attr("disabled", true);
+			initialiseTimer();
+		}
+		else {
+			if (isPaused) {
+				isPaused = false;
+				$btn_start.attr("disabled", true);
+			}
+			else {
+				isPaused = true;
+				$btn_start.attr("disabled", false);
+				paused_time = currentIncrement;
+			}
+		}
+	});
+
+	// Stop the timer and save the time logged by the timer on click of 'Complete' button
+	dialog.set_primary_action(__("Complete"), function() {
+		var grid_row = cur_frm.fields_dict['time_logs'].grid.grid_rows_by_docname[row.name];
+		grid_row.doc.completed = 1;
+		// grid_row.doc.expected_hours = args.expected_hours;
+		grid_row.doc.hours = currentIncrement / 3600;
+		grid_row.doc.to_time = frappe.datetime.now_datetime();
+		grid_row.refresh();
+		// Save the form
+		frm.save();
+		reset();
+		dialog.hide();
+	})
+	function initialiseTimer() {
+		interval = setInterval(function() {
+			if (isPaused) return;
+			var current = setCurrentIncrement();
+			updateStopwatch(current);
+		}, 1000);
+	}
+
+	function updateStopwatch(increment) {
+		var hours = Math.floor(increment / 3600);
+		var minutes = Math.floor((increment - (hours * 3600)) / 60);
+		var seconds = increment - (hours * 3600) - (minutes * 60);
+
+		// If modal is closed by clicking outside anywhere the modal, reset the timer
+		if (!$('.modal-dialog').is(':visible')) {
+			reset();
+		}
+		if(hours > 99)
+		reset();
+		$(".hours").text(hours < 10 ? ("0" + hours.toString()) : hours.toString());
+		$(".minutes").text(minutes < 10 ? ("0" + minutes.toString()) : minutes.toString());
+		$(".seconds").text(seconds < 10 ? ("0" + seconds.toString()) : seconds.toString());
+	}
+
+	function setCurrentIncrement() {
+		currentIncrement += 1;
+		return currentIncrement;
+	}
+
+	function reset() {
+		currentIncrement = 0;
+		isPaused = true;
+		initialised = false;
+		clearInterval(interval);
+		$(".hours").text("00");
+		$(".minutes").text("00");
+		$(".seconds").text("00");
+		$btn_start.attr("disabled", false);
+	}
+}
\ No newline at end of file