Най-малкият офис пакет в целия свят

Оригиналът е на Serge Zaitsev

6
2950

Навярно на всички е познат традиционния офис пакет – текстов редактор, електронна таблица, програма за правене на презентации и може би приложение за създаване на диаграми и водене на бележки. Всичко това можем да видим в Microsoft Office и в Google Docs. Всичките тези програми са мощни, но и много обемни. Какво ли е минималното количество код, необходимо за създаването на офисен пакет?

Платформата

Очевидно е, че нашият офисен пакет няма да е десктоп приложение със собствен GUI – за създаването на нещо подобно е необходим много код и много труд. Същото важи и за мобилните приложения. Можем да си помислим за създаването на някое терминално приложение и всъщност, има немалко абсурдно малки текстови редактори и електронни таблици, но ние ще постъпим по друг начин и силно ще си опростим работата, понеже за платформа ще използваме стандартен браузър.

Във всички браузъри е вграден, може да се каже, много достоен текстов редактор с много добри възможности за форматиране (contenteditable) и за изчисления на различни математически функции.

Колко малък може да е кодът за използване на този вграден офис пакет в браузъра? Много малък!

Текстовият редактор

Това е ‘приложение’, което аз използвам от много години:

<html contenteditable>

Да, това е всичко. Нещо повече, горният израз може да бъде превърнат в стандартен URL и аз именно това използвам за чернова, на която набързо да въведа някои свои бележки, мисли и т.н.:

data:text/html,<html contenteditable>

Копирайте по-горния ред и го въведете в адресния ред. Ако браузърът позволи въвеждането на подобен URL (всички по-популярни браузъри позволяват това), то можете да използвате клавишните комбинации Ctrl+B и Ctrl+I, за да зададете дали текстът да бъде получер или курсив.

Можем да подобрим това ‘приложение’, като добавим стилове (наистина считам, че типографските подобрения са важни). Ще получим следния израз, който също може да бъде директно въведен в адресния ред:

data:text/html,<body contenteditable style="line-height:1.5;font-size:20px;">

Ето как изглежда всичко това в браузъра:

Забележете, че и проверката на правописа работи коректно за всички инсталирани в браузъра езици. Тук се използва Chrome 86

Аз добавих този текст в отметките и сега за достъп до този безтегловен редактор е достатъчно да натисна няколко бутони или да кликна два-три пъти с мишката.

Разбира се, този пример може да бъде разширен, като могат да се добавят различни стилове на заглавията, да се съставят списъци, да се правят отстъпи и т.н.

Записът на набрания по този начин текст може да стане чрез стандартния запис на страницата като HTML файл, а може и да се разпечати на принтер.

Презентации

Преди няколко години написах кода на един съвсем опростен инструмент за създаване на презентации, който всъщност е самостоятелен HTML файл. Той може да бъде редактиран (подобно на markdown текст) и се рендира като цветна презентация в стил Takahashi.

Но нека този път направим един WYSYWIG редактор на слайдове. Нека в началото да създадем няколко празни слайда, които могат да бъдат редактирани:

<body><script>
for (let i=0; i<50; i++) {
  document.body.innerHTML += `
    <div style="position:relative;width:90%;padding-top:60%;margin:5%;border:1px solid silver;page-break-after:always;">
      <div contenteditable style="outline:none;position:absolute;right:10%;bottom:10%;left:10%;top:10%;font-size:5vmin;">
      </div>
    </div>`;
}
</script>

Числото 50 е избрано произволно, като не бива да се забравя, че не си струва да се създават повече слайдове наведнъж. Всеки външен div е слайд с тънък контур. Трикът с width и padding-topе необходим за запазване съотношенията на страните на слайда. Пробвайте да промените някои от тези значения, за да видите какво влияние оказват тези промени на структурата. Всеки вътрешен div е опростен редактор на текст с достатъчно голям шрифт, който е удобен за четене от екрана, осветен от проектора.

Съвсем не е зле. Но на нас в слайдовете са ни необходими заглавия и списъци, нали така?

Нека сега да добавим горещи клавиши:

document.querySelectorAll("div>div").forEach(el => {
  el.onkeydown = e=> {
    // `code` will be false if Ctrl or Alt are not pressed
    // `code` will be 0..8 for numeric keys 1..9
    // `code` will be some other numeric value if another key is pressed
    // with Ctrl+Alt hold.
    const code = e.ctrlKey && e.altKey && e.keyCode-49;
    // Find the suitable rich text command, or undefined if the key
    // is out of range
    x = ["formatBlock", "formatBlock", "justifyLeft", "justifyCenter", 
         "justifyRight", "outdent", "indent", "insertUnorderedList"][n];
    // Find command parameter (only for 1 and 2)
    y = ["<h1>", "<div>"][n];
    // Send the command and the parameter (if any) to the editor
    if (x) {
      document.execCommand(x, false, y);
    }
  };
});

Сега при натискането на Ctrl+Alt+1 в слайда, маркираният текст ще се превърне в заглавие. А ако натиснем Ctrl+Alt+2, то той отново ще стане обикновен текст. Комбинациите Ctrl+Alt+3 и Ctrl+Alt+5 променят подравняването, а 6 и 7 – отстъпа. Когато в края имаме 8, започва списък. Деветката е оставена за лични нужди и може да се настройва от потребителя. Пълният списък на contenteditable операциите е даден в MDN.

Ако махнем форматирането на показания по-горе код и го превърнем в data URL, то ще получим файл на редактор на слайдове с възможност за форматиране и големина около 600 байта:

data:text/html,<style>@page{size: 6in 8in landscape;}</style><body><script>d=document;for(i=0;i<50;i++)d.body.innerHTML+='<div style="position:relative;width:90%;padding-top:60%;margin:5%;border:1px solid silver;page-break-after:always;"><div contenteditable style="outline:none;position:absolute;right:10%;bottom:10%;left:10%;top:10%;font-size:5vmin;"></div></div>';d.querySelectorAll("div>div").forEach(e=>e.onkeydown=e=>{n=e.ctrlKey&&e.altKey&&e.keyCode-49,x=["formatBlock","formatBlock","justifyLeft","justifyCenter","justifyRight","outdent","indent","insertUnorderedList"][n],y=["<h1>","<div>"][n],x&&document.execCommand(x,!1,y)})</script>

Файлът може да бъде записан във вид на HTML файл и отворен с браузъра, за да се използва.

Създадените с негова помощ слайдове могат да се експортират чрез подсистемата за печат в PDF формат и да се показват на всеки компютър или проектор.

Опростено рисуване

Преди време предложих проекта onthesamepage.online за бързо създаване на чернови на различни идеи съвместно с други хора, но въпреки неговата простота, той все пак е много по-голям и излиза от рамките на една статия.

В качеството на абсолютен минимум можем само да чертаем линии с помощта на canvas. Трябват ни няколко canvas елемента, обработката на някои действия с мишката и флаг, който да показва, че следващите движения с натиснат бутон на мишката трябва да се приемат за рисуване.

Тук можем да уточним, че достъпът до елементите от id можем да получим чрез window[id] или window.id. Интересно е, че този подход дълго време не се приемаше и бе хакерски номер за IE, но сега вече е стандарт.

Освен това, реших да преместя обработката на положението на курсора на мишката в отделни къси функции, за да мога по-удобно да използвам mousedown и mousemove. Също така, махнах границите на елемента body, за да може canvas-ът да излезе на цял екран.

Кодът не е голям и заема само 400 байта:

<canvas id="v">
<script>
d=document, // shortcut for document
d.body.style.margin=0, // reset style
f=0, // mouse-down flag
c=v.getContext("2d"), // canvas context
v.width=innerWidth, // make canvas element fullscreen
v.height=innerHeight,
c.lineWidth=2, // make lines a bit wider
x=e=>e.clientX||e.touches[0].clientX, // get X position from mouse/touch
y=e=>e.clientY||e.touches[0].clientY, // get Y position from mouse/touch
d.onmousedown=d.ontouchstart=e=>{f=1,e.preventDefault(),c.moveTo(x(e),y(e)),c.beginPath()},
d.onmousemove=d.ontouchmove=e=>{f&&(c.lineTo(x(e),y(e)),c.stroke())},
d.onmouseup=d.ontouchend=e=>f=0
</script>

А ето как изглежда същия код, но форматиран само в един ред:

data:text/html,<canvas id="v"><script>d=document,d.body.style.margin=0,f=0,c=v.getContext("2d"),v.width=innerWidth,v.height=innerHeight,c.lineWidth=2,x=e=>e.clientX||e.touches[0].clientX,y=e=>e.clientY||e.touches[0].clientY,d.onmousedown=d.ontouchstart=e=>{f=1,e.preventDefault(),c.moveTo(x(e),y(e)),c.beginPath()},d.onmousemove=d.ontouchmove=e=>{f&&(c.lineTo(x(e),y(e)),c.stroke())},d.onmouseup=d.ontouchend=e=>f=0</script>

Електронни таблици

Това приложение ще бъде най-сложното и най-голямото, но няма да превиши лимита от 1 KB, какъвто поставят в редица конкурси по програмиране.

Структурата не е сложна. В HTML си  има таблици, така че защо да не ги използваме? Тъй като клетките на електронната таблица обикновено се адресират чрез буква+число, то ние ще ограничим нашата електронна таблица до размер 26х100 клетки. Най-логично е да създадем тези редове от клетки динамично, в цикъл. А една опростена стилизация ще направи тази електронна таблица много по-красива и приятна за гледане:

<table id="t">

t.style.borderCollapse="collapse"; // remove gaps between cells
for (let i = 0; i < 101; i++) {
  const row = t.insertRow(-1);
  for (let j = 0; j < 27; j++) {
    // convert column index j to a letter (char code of "A" is 65)
    const letter = String.fromCharCode(65+j-1); // 1=A, 2=B, 3=C etc
    const cell = row.insertCell(-1);
    cell.style.border = "1px solid silver"; // make thin grey border
    cell.style.textAlign = "right";         // right-align, like excel
    if (i > 0 && j > 0) {
      // add identifiable input field, this is where formula is entered
      const field = document.createElement('input');
      field.id = letter + i; // i.e, "B3"
      cell.appendChild(field);
    } else if (i > 0) {
      // Row numbers
      cell.innerHTML = i;
    } else if (j > 0) {
      // Column letters
      cell.innerHTML = letter;
    }
  }
}

Сега вече имаме голяма мрежа от клетки с редове и колони. Ще трябва да добавим нещо, което да изчислява математическите изрази. Можем да напишем един мръсен, но работещ код за тези изчисления, който да включва три масива – масив за всичките полета за въвеждане (в който ще получаваме въведените от потребителя значения и формули), масив за извикване на eval() (ако е необходима променлива и тя е свързана с формула) и кеш за последно въведените значения за всяко едно поле:

inputs = []; // assume that we did inputs.push(field) for each field in the loop above
data = {}; // smart data accessing object
cache = {}; // cache

// Re-calculate all fields
const calc = () => {
  inputs.map(field => {
    try {
      field.value = D[field.id];
    } catch (e) { /* ignore */}
  });
};

// We also need to customize our field initialization code:
field.onfocus = () => {
  // When element is focused - replace its calculated value with its formula
  field.value = cache[field.id] || "";
};
field.onblur = () => {
  // When element loses focus - put formula in cache, and re-calculate everything
  cache[field.id] = field.value;
  calc();
};
// Smart getter for a field, evaluates formula if needed
const get = () => {
  let value = cache[field.id] || "";
  if(value.chatAt(0) == "=") {
    // evaluate the formula using "with" hack:
    with(data) return eval(value.substring(1));
  } else {
    // return value as it is, convert to number if possible:
    return isNaN(parseFloat(value)) ? value : parseFloat(value);
  }
};
// Add smart getter to the data array for both upper and lower case variants:
Object.defineProperty(data, field.id, {get}),
Object.defineProperty(data, field.id.toLowerCase(), {get})

Сега електронната таблица трябва да работи – ако например, въведем 42 в А1 и „=A1+3“ в клетката А2, то като преместим фокуса към А2 трябва да видим 45.

След внимателна минимизация на кода ще се получи работеща електронна таблица с големина около 800 байта:

data:text/html,<table id="t"><script>for(I=[],D={},C={},calc=()=>I.forEach(e=>{try{e.value=D[e.id]}catch(e){}}),t.style.borderCollapse="collapse",i=0;i<101;i++)for(r=t.insertRow(-1),j=0;j<27;j++)c=String.fromCharCode(65+j-1),d=r.insertCell(-1),d.style.border="1px solid #ccc",d.style.textAlign="right",d.innerHTML=i?j?"":i:c,i*j&&I.push(d.appendChild((f=>(f.id=c+i,f.style.border="none",f.style.width="4rem",f.style.textAlign="right",f.onfocus=e=>f.value=C[f.id]||"",f.onblur=e=>{C[f.id]=f.value,calc()},get=()=>{let v=C[f.id]||"";if("="!=v.charAt(0))return isNaN(parseFloat(v))?v:parseFloat(v);with(D)return eval(v.substring(1))},Object.defineProperty(D,f.id,{get:get}),Object.defineProperty(D,f.id.toLowerCase(),{get:get}),f))(document.createElement("input"))))</script>

Сериозно?

Разбира се, това по никакъв начин не може да замести един истински офис пакет. Но е един добър пример за извършването на много действия с минимален размер на кода. Всички тези ‘приложения’ са ефимерни и губят своето състояние при обновяване на страницата. Но те могат да се съхраняват във вид на удобни отметки, които дават възможност за бързо нахвърляне на текст и за някои изчисления. Има друг момент – тези малки приложения строго спазват конфиденциалността и никъде не подават вашите данни.

Да, тези програми повече приличат на шега, отколкото на сериозни приложения, но аз направих едно хранилище с подобни неща, където всеки може да се запознае с използваните техники и да ги прилага при нужда.

0 0 глас
Оценете статията
Абонирай се
Извести ме за
guest
6 Коментара
стари
нови оценка
Отзиви
Всички коментари