oficina / sheets Public

Clone
git clone https://amadan.net/oficina/sheets

Plain git — no account needed to clone.

Download

Download this file

1/* The name box and the formula bar.
2 *
3 * The formula bar shows the selected cell's INPUT while the grid shows
4 * its computed value. That difference is the whole point: it is what
5 * makes this a spreadsheet rather than a table of numbers, and it is
6 * the reliable way to read a formula — the in-cell editor swap is best
7 * effort by design.
8 *
9 * Nothing here evaluates or formats anything. Committing posts the raw
10 * text and paints whatever the server sends back.
11 */
12(function () {
13 'use strict';
14
15 var box = document.querySelector('[data-name-box]');
16 var bar = document.querySelector('[data-formula-input]');
17 var size = document.querySelector('[data-selection-size]');
18 if (!bar && !box) return;
19
20 /* colName turns 0 into A, 25 into Z, 26 into AA. Written out rather
21 * than borrowed from the library so the name box agrees with the
22 * column headers even if the vendored grid is ever swapped. */
23 function colName(c) {
24 var s = '';
25 c += 1;
26 while (c > 0) {
27 var rem = (c - 1) % 26;
28 s = String.fromCharCode(65 + rem) + s;
29 c = Math.floor((c - 1) / 26);
30 }
31 return s;
32 }
33
34 function colIndex(name) {
35 var n = 0;
36 for (var i = 0; i < name.length; i++) {
37 n = n * 26 + (name.charCodeAt(i) - 64);
38 }
39 return n - 1;
40 }
41
42 function refOf(sel) {
43 var a = colName(sel.c) + (sel.r + 1);
44 if (sel.r === sel.r2 && sel.c === sel.c2) return a;
45 return a + ':' + colName(sel.c2) + (sel.r2 + 1);
46 }
47
48 /* parseRef accepts A1 or A1:C3, case-insensitively. null for anything
49 * else, which the caller turns into "put back what was there" — a
50 * name box that silently jumped somewhere on a typo would be worse
51 * than one that appears to ignore you. */
52 function parseRef(text) {
53 var m = /^\s*([A-Za-z]{1,3})(\d{1,7})(?::([A-Za-z]{1,3})(\d{1,7}))?\s*$/.exec(text);
54 if (!m) return null;
55 var out = {
56 c: colIndex(m[1].toUpperCase()), r: parseInt(m[2], 10) - 1,
57 c2: colIndex((m[3] || m[1]).toUpperCase()), r2: parseInt(m[4] || m[2], 10) - 1
58 };
59 var g = window.SheetsGrid;
60 if (out.r < 0 || out.c < 0) return null;
61 if (g && (out.r2 >= g.rows || out.c2 >= g.cols)) return null;
62 return out;
63 }
64
65 function describe(sel) {
66 if (!size) return;
67 var n = (Math.abs(sel.r2 - sel.r) + 1) * (Math.abs(sel.c2 - sel.c) + 1);
68 /* The COUNT, never a total. A sum here would be the client doing
69 * arithmetic on strings the server rendered for display, which is
70 * exactly the business this design keeps out of JavaScript. */
71 size.textContent = n > 1 ? n + ' cells' : '';
72 }
73
74 function ready() {
75 var g = window.SheetsGrid;
76 if (!g) return;
77
78 g.onSelection(function (sel) {
79 if (!sel) return;
80 if (box && document.activeElement !== box) box.value = refOf(sel);
81 if (bar && document.activeElement !== bar) bar.value = g.rawAt(sel.r, sel.c);
82 describe(sel);
83 });
84
85 if (box) {
86 box.addEventListener('keydown', function (e) {
87 if (e.key === 'Escape') {
88 var sel = g.selection();
89 if (sel) box.value = refOf(sel);
90 g.focus();
91 return;
92 }
93 if (e.key !== 'Enter') return;
94 e.preventDefault();
95 var want = parseRef(box.value);
96 var ws = g.worksheet();
97 if (!want || !ws || !ws.updateSelectionFromCoords) {
98 var back = g.selection();
99 if (back) box.value = refOf(back);
100 return;
101 }
102 ws.updateSelectionFromCoords(want.c, want.r, want.c2, want.r2);
103 g.focus();
104 });
105 }
106
107 if (!bar || bar.readOnly) return;
108
109 bar.addEventListener('keydown', function (e) {
110 // Keep the grid's document handler from treating bar typing as a cell edit.
111 e.stopPropagation();
112 var sel = g.selection();
113 if (!sel) return;
114
115 if (e.key === 'Escape') {
116 e.preventDefault();
117 bar.value = g.rawAt(sel.r, sel.c);
118 g.focus();
119 return;
120 }
121 /* Enter commits and moves down, Tab commits and moves right —
122 * the two motions every spreadsheet has, and the reason typing a
123 * column of numbers never needs the mouse. */
124 var down = e.key === 'Enter';
125 var right = e.key === 'Tab' && !e.shiftKey;
126 if (!down && !right) return;
127 e.preventDefault();
128
129 g.write(sel.r, sel.c, bar.value);
130 var ws = g.worksheet();
131 if (ws && ws.updateSelectionFromCoords) {
132 var r = Math.min(sel.r + (down ? 1 : 0), g.rows - 1);
133 var c = Math.min(sel.c + (right ? 1 : 0), g.cols - 1);
134 ws.updateSelectionFromCoords(c, r, c, r);
135 }
136 g.focus();
137 });
138 }
139
140 if (window.SheetsGrid) ready();
141 else document.addEventListener('sheets:ready', ready, { once: true });
142}());
143