(function (win) {
    win.CharCounter = _ = {
        Init: function (globalEventHandlerRegistrationFunction, eventCallbacks) {
            if (globalEventHandlerRegistrationFunction) {
                var cls = "counter";
                globalEventHandlerRegistrationFunction("keyup", onKeyUp, cls);
                globalEventHandlerRegistrationFunction("focusin", onFocusIn, cls);
                globalEventHandlerRegistrationFunction("focusout", onFocusOut, cls);
            } else {
                $("input.counter")
                    .not(".counter-init")
                    .addClass("counter-init")
                    .keyup(ShowCounter)
                    .focus(ReShowCounter)
                    .blur(HideCounter);
                $("textarea.counter")
                    .not(".counter-init")
                    .addClass("counter-init")
                    .keyup(ShowCounter)
                    .focus(ReShowCounter)
                    .blur(HideCounter);
            }

            AssignCallbacks(eventCallbacks);
        },

        StateEnum: {
            notInitialized: 0,
            notShown: 1,
            basic: 2,
            highlighted: 3,
            warn: 4,
            max: 5,
            exceeded: 6
        },

        AssignEventCallback: function (eventName, callbackFunction) {
            if (eventName) {
                var obj = {};
                obj[eventName] = callbackFunction;
                AssignEventCallbacks(obj);
            }
        },

        Refresh: function (element) {
            if (typeof element == "string" && element.length && element.substr(0, 1) != "#")
                element = "#" + element;
            element = $(element || $("body"));
            var tag = element[0].tagName.toUpperCase(),
                list;

            if (((tag == "INPUT" && element.attr("type") == "text") || tag == "TEXTAREA") && element.hasClass("counter"))
                list = [element];
            else 
                list = element.find("input[type=text].counter,textarea.counter");

            if (list.length) {
                $.each(list, function () {
                    var el = $(this);
                    ShowCounter(el);
                    if (!el.is(":focus"))
                        HideCounter(el, true);
                });
            }
        }
    };

    $.fn.extend({
        hasClasses: function (selectors) {
            var self = this;
            for (var i in selectors) {
                if ($(self).hasClass(selectors[i]))
                    return true;
            }
            return false;
        }
    });
    function AssignCallbacks(defObj) {
        if (defObj) {
            var obj = m_oCallbacks,
                p;
            for (p in defObj) {
                if (typeof obj[p] == "function")
                    obj[p] = defObj[p] || Dummy;
            }
        }
    }

    function HideCounter(el, skipEvent) {
        var data = el.data("counter-data"),
            cbk;
        if (data && data.isShown && (!data.isTextArea || el.hasClass("counter-hide-on-blur"))) {
            el.css("padding-right", data.origPadding);
            el.next().hide();
            data.hidden = true;
            cbk = m_oCallbacks;
            if(cbk.shown && !skipEvent)
                cbk.shown(el, data);
        }
    }

    function ReShowCounter(el) {
        var data = el.data("counter-data"),
            counter = el.next(),
            cbk;
        if (data) {
            ShowCounter(el);
            el.css("padding-right", data.padding);
            if (counter.length)
                counter[0].style.removeProperty("display");
            data.hidden = false;
            if (data.isShown && (!data.isTextArea || el.hasClass("counter-hide-on-blur"))) {
                cbk = m_oCallbacks;
                if (cbk.shown)
                    cbk.shown(el, data);
            }
        }
    }

    function ShowCounter(el) {
        var counter = el.next(),
            origData = el.data("counter-data"),
            prev = PrevData(origData),
            data = CharThresholds(el, origData),
            count,
            divs,
            parent;

        if (!counter.length) {
            parent = el.parent();
            if (parent.css("position") == "static")
                parent.css("position", "relative");
            counter = $("
" + data.maxAt + "
"); el.after(counter); count = counter.find(">div"); divs = count.find("div"); if (!data.isTextArea && el.hasClass("counter-vertical")) { counter.addClass("counter-vertical"); divs.css("display", "block"); } else { divs.css("display", "inline-block"); } } else { count = counter.find(">div"); divs = count.find("div"); } var v = el.val(), len = v.length, hasMax = data.maxAt != 0, hasWarn = data.warnAt > -1, hasHl = data.highlightAt > -1, hasBasic = data.basicAt > -1, add = data.stopAtMax ? 0 : 1, showMax = hasMax && len >= (data.maxAt + add), showWarn = !showMax && hasWarn && len < (data.maxAt + add) && len >= data.warnAt, showHl = !showMax && !showWarn && hasHl && len < data.warnAt && len >= data.highlightAt, showBasic = !showMax && !showWarn && !showHl && hasBasic && (!hasHl || len < data.highlightAt) && len >= data.basicAt, classes = [data.basicClass, data.hlClass, data.warnClass, data.maxClass], wasShown = counter.hasClasses(classes), showAny = showBasic || showHl || showWarn || showMax, visibilityChange = showAny != wasShown; if (!data.maxAt || data.maxAt < 0) return; // Do not show the counter as the required max length has net been defined. data.isShown = showAny; data.isBasic = showBasic; data.isHighlighted = showHl; data.isWarn = showWarn; data.isMax = showMax; data.prevCount = prev.count; data.prevRemainingCount = prev.remaining; data.prevState = prev.state; ExecuteCallbacks(el, prev, data, showAny, showBasic, showHl, showWarn, showMax); divs.eq(0).text(data.curCount); if (!data.isTextArea || !visibilityChange || showAny) { if (data.isTextArea && showAny) counter.slideDown(); counter .toggleClass(data.maxClass, showMax) .toggleClass(data.warnClass, showWarn) .toggleClass(data.hlClass, showHl) .toggleClass(data.basicClass, showBasic); } if (data.isTextArea) { if (wasShown != showAny) { if (showAny) counter.hide().slideDown(); else counter.slideUp(function () { counter.removeClass(classes); }); } } if (!data.isTextArea) { data.padding = counter.width() + 20; el.css("padding-right", data.padding); } } function CharThresholds(el, data) { var len = el.val().length; if (!data) { var def = el.data("counter-def"), vals, max = el.attr("maxlength"), isTextArea = el[0].nodeName.toUpperCase() == "TEXTAREA"; if (max) max *= 1; if (def) vals = def.split(","); else vals = []; data = { id: el.attr("id"), basicAt: CalcThreshold(max, vals, 0, isTextArea ? "50%" : "x"), highlightAt: CalcThreshold(max, vals, 1, "80%"), warnAt: CalcThreshold(max, vals, 2, isTextArea ? "90%" : -3), maxAt: CalcThreshold(max, vals, 3, max), stopAtMax: max || (vals && vals.length && vals.length > 3 && vals[3] && vals[3] > 0), basicClass: CounterClass(vals, 4, "counter-basic"), hlClass: CounterClass(vals, 5, "counter-hl"), warnClass: CounterClass(vals, 6, "counter-warn"), maxClass: CounterClass(vals, 7, "counter-max"), isTextArea: isTextArea, origPadding: el.css("padding-right"), prevCount: -1, prevRemainingCount: 0, remainingCount: 0, prevState: _.StateEnum.notInitialized, curState: _.StateEnum.notInitialized, isShown: false, isBasic: false, isHighlighted: false, isWarn: false, isMax: false }; el.data("counter-data", data); if (data.stopAtMax) el.attr("maxlength", data.maxAt); } data.curCount = len; data.remainingCount = data.maxAt - len; data.isExceeded = data.remainingCount >= 0; return data; } function CounterClass(vals, idx, defaultVal) { return vals && vals.length > idx && vals[idx].trim().length ? vals[idx].trim() : defaultVal; } function CalcThreshold(max, vals, idx, defaultVal) { if (!vals) return (defaultVal * 1) || -1; else { if (vals.length < (idx + 1) || !vals[idx].length) vals[idx] = defaultVal + ""; var val = vals[idx].trim(), neg = false, m = max || Math.abs(!isNaN(vals[3]) ? vals[3] * 1 : 0); if (isNaN(val)) return -1; if (val.substr(0, 1) == "-") { neg = true; val = val.substr(1); } if (val.substr(val.length - 1) == "%") { if (m > 0) { val = Math.round(m * (val.substr(0, val.length - 1) / 100)); } else { return -1; } } if (neg && idx < 3) { // idx == 0 means that val is the max value which, if negative, just means that it is a hard stop and we still return the absolute value of if (m > 0) return m - val; else return -1; } else return val * 1; } } function PrevData(data) { var prev = m_oPrevData; if (data) { prev.count = data.curCount; prev.remaining = data.remainingCount; prev.state = data.curState; prev.shown = data.isShown; prev.basic = data.isBasic; prev.hl = data.isHighlighted; prev.warn = data.isWarn; prev.max = data.isMax; prev.exceeded = data.isExceeded; } else { prev.count = -1; prev.remaining = -1; prev.state = _.StateEnum.notInitialized; prev.shown = false; prev.basic = false; prev.hl = false; prev.warn = false; prev.max = false; prev.exceeded = false; } return prev; } function ExecuteCallbacks(el, prev, cur, showAny, showBasic, showHl, showWarn, showMax) { var cbk = m_oCallbacks, count = prev.count != cur.curCount, state = prev.state != cur.curState, shown = showAny != (prev.basic || prev.hl || prev.warn || prev.max), basic = prev.basic != showBasic, hl = prev.hl != showHl, warn = prev.warn != showWarn, max = prev.max != showMax, exceeded = prev.exceeded != cur.isExceeded; if (count) cbk.countChanged(el, cur); if (state) cbk.stateChanged(el, cur); if (shown) cbk.shown(el, cur); if (basic) cbk.basic(el, cur); if (hl) cbk.highlighted(el, cur); if (warn) cbk.warn(el, cur); if (max) cbk.warn(el, cur); if (exceeded) cbk.exceeded(el, cur); } })(window);
Table of Contents
Obsah

Explaining to a layman how long (good) programming really takes

Jak vysvětlit laikovi jak dlouho programování (kvalitního) softwaru opravdu trvá

A.k.a "No, I really cannot build that in a day, mum."

Aneb "Ne mami, opravdu tohle nemůžu vytvořit za den."

by Marek Vsechovsky, October 2020

Napsal Marek Všechovský, Listopad 2020

During my long time in professional programming I've encountered many people to whom I ended up trying to explain how much time programming really takes and why. People have NO IDEA what the reality in this field is.

Během mé dlouhé profesionální kariéry softwarového vývojáře jsem se setkal s mnoha lidmi, kterým jsem se v určitou chvíli snažil vysvětlit, jak moc časově náročné programování ve skutečnosti je a proč. Lidé nemají NEJMENŠÍ POTUCHU jaká je realita v tomto odvětví.

If you ever wondered yourself, this article is for you.

Pokud jsi nad tím někdy přemýšlel(a), tento článek je pro tebe.

And if you are a programmer yourself, maybe it will help you because now you'll have a link to give to someone to check out if you find yourself in the same situation trying to explain the same thing. People, generally, really have no idea regarding this that is anywhere close to reality.

A pokud jsi sám programátorem, možná ti tento článek pomůže tím, že budeš mít k dispozici odkaz, který budeš moci lidem nabídnout, pokud se nalezneš ve stejné situaci. Lidé obecně opravdu nemají ohledně tohoto nejmenší tušení, které je jakkoliv blízko k realitě.

Explaining this well is a hard task. Even us - the very people who should know better - more often than not underestimate, sometimes very grossly, how much time we will need to code a particular feature. We even sometimes doubt the abilities of other programmers if they tell us that:
"This or that feature took me this long to do.".
"Why so long? I'd surely be able to do it in half - quarter - one tenth of the time."

Vysvětlit toto je těžké. Dokonce i my programátoři, tedy lidé, co by měli mít tu nejlepší představu, podhodnocujeme naše časové odhady k implementaci nové funkce programu. A to děláme překvapivě velice často, aniž by jsme to dělali záměrně. Dokonce někdy i zapochybujeme o schopnostech jiného programátora když nám řekne:
"Tohle nebo tamto mi trvalo naprogramovat tolik a tolik hodin, dní, týdnů...".
"Proč tak dlouho? To bych zvládl napsat za polovičku - čtvrtinu - desetinu toho času."

But that is often only until we try to do it ourselves and discover the many gotchas and tiny little but necessary features that take hours to implement to make the result actually really good.

Ale tenhle názor nám mnohdy vydrží jen do chvíle, dokud to nezkusíme naprogramovat sami, nebo se pokusíme existující kód vylepšit. Kód jde vylepšit vždy, ale často nám docvaknou časově náročné maličkosti a záludnosti, které nás rovnou nenapadly. A aby byl výsledek naší práce opravdu dobrý, je to hodně právě o těch "neviditelných" maličkostech.

Anyway, one of the eternal laymen doubters seem to be my mother. I can explain a hundred times and give examples that for instance, as I read somewhere at some point few years back, Microsoft has / had at that time about 100 developers working on just MS Word...

"You know, mum, that thing that lets you type text... The thing that has allowed you to do that since 1983. And yet, more than 40 years later, they are still adding features. Features that most people never use or even realise they exist. And one hundred people work on that every day today and they were getting to that number steadily since 1983!! Imagine the number of man-hours worked on that mum!! Can you imagine? They do as much work every 3.5 days as a single person could only be able to do in a whole year if that person worked including all weekends and took no holidays whatsoever! I'm working on my projects alone. So I can do 1 man-year of work in... well, about one year. (Well, maybe quite a bit less due to the amount of time dedicated to it, the lack of necessary team meetings and no other management and team synchronization tasks involved, but that's beside the point. I still cannot do anywhere close as much work as 100 people in the same amount of time. That should be pretty clear. On the other hand, I do have to take care of all the other aspects of running a company like purchases, bookkeeping, government reporting, server setup and maintenance etc.) And you are surprised that my big new project that I'm writing from scratch is not finished in 2 months yet?" :-)

No každopádně jedním z těch věčných pochybovačů se zdá být moje mamka. Můžu se to pokusit vysvětlit stokrát a dát nekonečně mnoho příkladů. Například ten, kde, jak jsem před lety četl, Microsoft má / měl v ten čas zhruba 100 programátorů pracujících jen na textovém editoru MS Word...

"Ten mami znáš ne?... Ten program co umožňuje lidem psát text na počítači už od roku 1983. A přesto, o více jak 40 let později, stále ještě do něj přidávají funkce. Funkce, které většina lidí nemá ani ponětí, že v tom programu existují. A STO lidí pracuje jen na tomhle ještě dnes. A ten počet lidí se postupně zvyšuje už od roku 1983!! Mami, představ si ten počet odpracovaných pracovních hodin jen na tomhle jednom jediném, na první pohled jednoduchém, programu!! Fakt, zkus si to opravdu představit!! Umíš si to představit? Oni za 3 a půl dne odpracují tolik hodin, kolik by zvládl osamocený člověk odpracovat za celý rok, i kdyby dělal všechny víkendy a neměl vůbec žádnou dovolenou! Já jsem takový osamocený člověk, který dělá na svých projektech zcela sám od nápadu přes design a implementaci až ke kontrole kvality. Takže mohu odpracovat jeden pracovní rok... no, asi tak zhruba za rok. (No, možná ve skutečnosti docela znatelně méně díky tomu, že tomu věnuji asi více než délku běžné pracovní doby a díky tomu, že nemusím chodit na schůze, koordinovat svou práci s ostatními atd., ale to už je trošku mimochodem. Stále nemohu udělat ani z velkého daleka tolik práce jako 100 lidí za stejný časový úsek. To by mělo být celkem jasné. A sice nemám schůze atp., zato se musím starat o firmu, starat se o provoz serverů, platit věci, dělat účetnictví, reportovat pravidelně státu, nakupovat sám, co potřebuji neb nemůžu jen říci nějakému firemnímu nákupnímu oddělení nebo manažerovi atd. atd.) A ty jsi překvapená že můj velký softwarový projekt který designuji a programuji z gruntu a sám ještě není za 2 měsíce hotový?" :-)

Well, needless to say that this explanation does not seem to help as I'm still hearing the same surprise from my mum now and then yet again and again.

No, asi už teď nemusím opakovat, že toto vysvětlení se nezdá, že by nějak obzvláště pomáhalo a já slýchám stejné překvapení od mé matky v průběhu let čas od času znovu a znovu.

And that is why I decided to write this article.

A to je přesně ten důvod, proč jsem se rozhodl napsat tento článek.

That and the fact that I have just finished working on a component that solves a problem that can basically be solved in just very few lines of code and HTML markup in just about 10 minutes and yet it took me 3 whole days to do! Here is why...

To a ten fakt, že jsem právě dokončil práci na nové komponentě, která řeší problém, který může být v podstatě vyřešen v pouhých pár řádcích kódu ve zhruba 10ti minutách a přesto mi její naprogramování zabralo celé 3 dny! Zde je odpověď na otázku "Jak to!?"...

Intro

Úvod

What I'm going to do here is show the complete code of, at a first glance, a very simple component - a text box character counter.

Co tu udělám je, že předvedu kód na první pohled velice jednoduché komponenty - počítadla napsaných znaků.

I'll show both the simple version and my version made into a reusable component.

Ukáži jak jednoduchou variantu, tak mou verzi naprogramovanou jako opakovatelně využitelnou komponentu.

I will be targeting any explanations mostly at lay people. I will show the complete code, but it is not important to understand the code itself at all. What I want to offer here is a comparison of the amount of effort needed between doing something very simply and making the same thing nice, reusable and intelligent.

Má vysvětlení budu pak směřovat na naprosé lajky, takže mému vyvětlení by měl rozumět každý. Nejde tu o to porozumět kódu, ale o získání představy o programování jako lidské činnosti obecně. Ukáži zde kompletní kód, ale není vůbec důležité mu rozumět. O to tu nejde. Co zde chci nabídnout je srovnání množství vyžadovaného úsilí potřebného mezi programováním čehosi co možná nejjednodušeji a toho samého tak, aby to vypadalo hezky a bylo to jednoduše využitelné znovu a znovu v dalších programech jako komponenta.

I will also show how the component looks, animate it to show its function and allow the user to interact with it as well, to appreciate the difference.

Také předvedu, jak moje komponenta vypadá, zanimuji jí, abych ukázal jak funguje a umožním vaší interakci s ní, abyste mohli ocenit ten rozdíl.

And yes, if you are a programmer and have a use case for this component, by all means go ahead and use my code.

A ano, pokud jsi sám/sama programátor(ka) a máš pro mou komponentu využití, klidně můj kód použij.

If you manage to read the article in its entirety, you will learn how easy some things can be to program but also how complicated the same things can get.

Když se vám podaří přečíst tento článek do jeho konce, uvidíte, jak jednoduché může být některé věci naprogramovat, ale také jak komplikovanými se ty samé věci mohou velice rychle stát, pokud chceme, aby se chovali a vypadali lépe.

Finally, I'd be happy if anyone - programmers or lay people - leave me a comment.

Nakonec, budu velice rád pokud mi kdokoliv z vás - jak programátoři tak lajci - zanecháte váš komentář.

The problem we are solving here

Problém, který tu řešíme

The goal for our article's example is to add a visible character counter after a multi-line text box to show the user both the maximum number of characters allowed and the currently entered number of characters. Something like this:

Cílem v tomto článku bude ukázat na konkrétním příkladu, jak se věci programují. Budeme tu řešit přidání viditelného počítadla znaků za multi-řádkové textové pole, kde toto počítadlo slouží jako vizuální zpětná vazba pro uživatele k tomu, aby viděl, kolik znaků je celkem v daném poli povoleno a kolik už jich napsal. Něco jako toto:

16 / 200

For the start I disabled the field, until we have talked about the code that will make it interactive.

Pro začátek jsem tomuto poli zakázal interaktivitu, dokud nebudeme hovořit o kódu, který mu umožní mít zamýšlené chování.

You've seen it on every second website you've ever encountered, right? If there is a form with a multi-line textbox into which you are supposed to type your message, comments, suggestions etc. they usually, if they have the number of total characters you can enter limited, put a character counter below or above the box to let you know how close you are to the limit. At least any SEMI-DECENT website has this feature on their limited multi-line text boxes. In its simplest form this takes a programmer just a few minutes to write.

Podobné textové pole vídáte na každých druhých webstránkách na které jste kdy zabrouzdali. Jestliže na nich narazíte na formulář, který obsahuje multi-řádkové textové pole do kterého se očekává, že napíšete například svou správu pro danou firmu, komentář, doporučení atd., pak obyčejně takové počítadlo u takového pole mají, pokud mají omezenou maximální délku textu, který od nás uživatelů akceptují. To abyste věděli, pokud se budete blížit k tomuto jejich limitu a že takový limit vůbec existuje. Tuto "slušnost" vůči uživateli neposkytují úplně všichni, ale každá POŘÁDNÉJŠÍ firma ano. Ve své nejjednodušší formě trvá programátorovi něco takového napsat jen pár minut.

Concrete real-life example

Příklad z reálného života

Yes, any DECENT company gives you this functionality. And courtesy.

Ano, každá POŘÁDNĚJŠÍ firma nám takovou funkčnost nabídne. A slušnost.


What follows in this aside sub-section is a description how even a very, very large and wealthy company can get something as simple as the component I'll be describing shortly, even in its simplest form, very horribly wrong.

Co následuje v této odbočce od hlavního tématu článku je popis, jak i velká, velmi velká a bohatá globální firma, může něco tak jednoduchého, jako je ta komponenta, kterou představím níže, i v její úplně nejjednodušší formě, naprosto strašlivým způsobem nezvládnout.

The described real-world website problems demonstrate that yes, you can write very bad code quickly. Code, that will infuriate the users. BUT!! It will apparently get you hired by one of the very biggest companies in the world. So there's that. :-)

Popisované zásadní nedostatky a z nich pro uživatele plynoucí problémy reálných web stránek demonstrují, že "Ano, programátor může zplodit velmi špatný kód velice rychle." Kód, který bude uživatele přivádět k šílenství. ALE!! Takový kód zjevně také člověku zaručí najmutí jednou z naprosto největších firem na světě, tak asi tak no... (ironie!)

In fact, you can be the fastest programmer in the world by avoiding issues and simply writing NO code whatsoever to resolve them. Now there's an idea!

Vlastně člověk může být nejrychlejším programátorem na světě, když se bude vyhýbat problémům a reději nebude psát ŽÁDNÝ kód, aby je vyřešil! To je nápad! Že mě to nenapadlo dřív!


Joking aside...

No ale teď bez vtipkování...

I think that especially on feedback forms the number of characters should not be limited or at least the limit should be very, very high, because otherwise you are potentially robbing yourself of helpful feedback and at the same time showing your customers that really, the feedback form is here just for show and you actually don't really care about some puny, insignificant user's feedback. And generally, "unlimited" feedback forms is what we find. But there is one huge exception that I know so intimately of. Yes Citibank, I'm talking about you!

Osobně jsem toho názoru, že na formulářích pro zpětnou vazbu od uživatele by maximální počet znaků neměl být omezen vůbec, a když už tam nějaký musí být, měl by být hodně vysoký, protože jinak se daná firma okrádá o potenciálně velmi prospěšnou zpětnou vazbu a zároveň ukazuje uživateli že ve skutečnosti tam takový formulář mají spíše jen tak pro formu, aby se neřeklo a že jí ve skutečnosti nějaké názory jejich prostých zákazníků nezajímají. A převážně na webu skutečně nacházíme takové formuláře s neomezeným počtem znaků. Ale já osobně vím o jedné veliké, převeliké vyjímce u firmy, kterou znám tak dobře, jejíž jsem mnoholetým zákazníkem. Ano Citi banko, mluvím o tobě!

OK, Citibank... In my eyes the worst ever company in the world. I do bank with them as they do have a couple of financial products that actually are the best of its kind available in Australia, but otherwise they show just how ignorant towards your customers you can afford to be if you are one of the very biggest corporations in existence on Earth. And on on top of that (and thanks to) being legally allowed to create untold billions of dollars from nothing, from thin air. That is, for anybody outside of the banking sector, called counterfeiting and should you try the same thing even with amounts infinitesimally smaller than that, you'd be hunted by law enforcement and locked up for a vary long time.

OK, Citi banka... V mých očích ta nejhorší firma na světě. Ne nezbytně pro produkty, které nabízejí, ale právě kvůli naprosto zjevnému nezájmu dělat věci ohledně online zákaznického servisu dobře. Mám u Citi účet, protože mají dva produkty, které jsou svého druhu nejlepší v Austrálii, ale jinak ukazují jak moc velký nezájem může mít firma vůči jejím zákazníkům, když je jednou z absolutně největších na světě. A navíc když má legálně povoleno vytvářet nekonečné miliony dolarů z ničeho, ze vzduchu, který dýcháme. Normálně se takové činnosti u obyčejných lidí říká padělání peněz. Zkuste to a bude vás honit po celém světě Interpol.

So this company, which creates money from nothing and then miraculously lends it to you and me by simply typing some numbers into our accounts on their computers, and then charge us interest for their trouble, effectively making us their slaves, working away to return them money that they never had in the first place, cannot apparently afford to pay any good programmers. Well, even bad programmers should be able to fix the following, very easily fixable, but major problems in like 10 minutes, but... But no! Instead Citibank seems to maybe pay a little bonus to one of their tellers and tell them that they want them to have a look at this book about programming for about a week and then program a website for them. I'm not even exaggerating too much. That is how it looks.

Takže tahle firma, která si tiskne peníze z ničeho a pak je zázračně půjčuje nám obyčejným smrtelníkům čistě jen tak, že napíše nějaká zcela nová čísla do počítače a ještě nám pak z těchto zcela předtím neexistujících peněz účtuje úroky, kromě toho, že tu původní vlastně imaginární půjčku musíme tvrdě odpracovat, si zjevně nemůže dovolit zaplatit alespoň průměrně dobré programátory. No, i špatný programátor by asi dokázal udělat tohle počítatlo znaků a vyřešit další problémy, které konkrétně jejich stránka zpětné zákaznické vazby má, tak zhruba za 10 minut, ale... Ale ne! Místo toho to spíše vypadá, že Citi banka zaplatí malý bonus úředníkovi za přepážkou a řekne mu, aby se přes víkend koukl na nějakou knihu o programování a pak pro ně udělal web stránky. A to ani moc nepřeháním. Tak jejich web skutečně vypadá na některých místech.

Consider this: I've been writing to them several times during the years, trying to give them good feedback and even suggest fixes. I wrote to them about the many, many blatant shortcomings of their internet banking website that I won't list here. Just know that half of them were basic, common-sense things that literally every other bank I ever banked with got right form the very start of them venturing into the great thing called the Internet. Hell, the other banks got right even the more difficult stuff right away.

Zvažte toto: Psal jsem jim v průběhu let několikrát, pokoušejíc se jim dát užitečnou zpětnou vazbu a doporučil dokonce konkrétní řešení. Psal jsem jim o mnoha, mnoha do očí bijících nedostatcích jejich stránek internetového bankovnictví, které tu nebudu vypisovat. Jen vězte, že polovička z nich byly běžné, selsko-rozumové věci které doslova každá jiná banka u které jsem kdy měl účet měla vyřešené správně už od samého začátku, kdy se pustili do toho úžasného online světa Internetu. K čertu, ty ostatní banky měli rovnou dobře provedené i ty více složité věci!

However, so far, I have been caught out every single time with yet another set of issues that their very feedback page was and most likely still is experiencing. The reason they caught me out repeatedly is partly due to me forgetting after a couple of years went by since my last attempt to help them with my feedback that this was happening on their customer feedback page and partly due to me thinking that they surely must have surely fixed by now those oh-so-simple-to-fix, but majorly annoying and unprofessional bugs. And these were / are not even bugs, no, they are obviously by design! A design by a dog probably... (No offence to dogs. I love dogs.)

Přesto, až doposud jsem se nechal nachytat pokaždé celým dalším souborem problémů přímo na jejich stránkách pro zákaznickou zpětnou vazbu. Důvodem, proč mě nachytali opakovaně je částečně to, že uběhli třeba dva roky od mého posledního pokusu jim pomoci poukázáním na věci, které musí iritovat spousty zákazníků a částečně proto, že já za tu dobu zapoměl, že jejich stránka na stížnosti sama trpí řadou základních a pekelně iritujících nedostatků a částečně proto, že jsem vždy věřil, že už přece museli dávno tyto zcela neprofesionální problémy odstranit, za ta léta. A ty problémy nejsou dokonce ani nechtěnými chybami, ale zjevně to chování bylo tak navržené! Navržené asi psem, nebo já nevím... (Bez urážky psům. :-) Miluju psy.)

Anyway, so you get to their hard to find feedback page and start writing. Since you are yourself a programmer you are able to describe the problem exactly and also offer solutions, since those obviously did not occur to the programer - sorry, the repurposed teller that glanced at a book about programming, probably. And then you want to send it...

Nicméně pokračujme. Konečně se dostanete do ukrutně těžce nalezitelné stránky pro zpětnou vazbu od zákazníků a začnete psát. Protože jste sami programátorem jste schopni popsat daný problém přesně a také nabídnout několik možných řešení, neb ta zcela zjevně nedocvakla danému programátorovi - omlouvám se, člověku převelenému od přepážky, který na se v rychlosti mrknul na nějakou knížku o programování. Možná. A poté, co své stížnosti a návrhy na řešení sepíšete, chcete je odeslat...

Oh how mistaken you were if you thought it was gonna be that simple. Stupid you. No, no no! First you are told by the page that your text contains characters that are not allowed. Helpfully, they tell you which they are. Among them, outrageously, is the apostrophe. You know, the symbol that is used in written English so often in abbreviations like "I'm", "it's", "didn't", "couldn't" and so on and on. It is almost like if they told you you cannot use the letters "A" and "B" when you are writing to them.

Óóó, v jakém omylu to žijete, pokud jste si mysleli, že to bude tak jednoduché. Najivkové. Ne, ne, ne! První vám ta web stránka sdělí, že jste v zadaném textu použili nepovolené znaky. Nápomocně vám sdělí dokonce i to, které že to jsou ty nepovolené znaky. Mezi nimi, k údivu, který vede k dokořán otevřené puse, je tam i apostrof. To je ten symbol používaný ve psané Angličtině ve výrazech jako je "I'm"!!, "it's", "didn't", "couldn't" a tak dále!! (Pro neangličtináře, tyto výrazy se objevují v každé druhé větě, zvlášť, když něco vysvětlujete!) Je to skoro to samé, jako by vám řekli, že nemůžete používat písmena "A" a "B" když jim píšete.

OK, it is what it is, so you are pushed to spend more of your time that you so generously gifted to them already while trying to help them with your feedback, and remove the apostrophes. Except it is not as simple as just removing them obviously, since you still want to be understood, so you change all the "I'm" to "I am", all the "couldn't" to "could not" etc. Try sending again. Oh, still some "bad, very bad characters" preventing you to send this, the page says. So rinse and repeat. Finally, you are all clear. Your ordinary english writing will not confuse their dumb software anymore. Great. Push "Send" and...

Fajn, je to holt jak to je a jste donuceni strávit více vašeho času, který jste jim už tak ochotně dosud věnovali v rámci vaší snahy jim pomoci a odstraníte všechny apostrofy. Kromě toho, že to není tak jednoduché, jako jen tyto znaky odstranit. Pořád si přejete, aby vám bylo rozuměno a tak musíte změnit všechny "I'm" na "I am", všechny "couldn't" na "could not" atd. Zkoušíte odeslat vyplněný formulář znovu. Áááh, stále tu máme nějaké špatné, velmi velmi špatné znaky, které odeslání brání, říká nám tato super hloupá stránka. Takže "vymáchat znovu a opakovat", jak se říká v angličtině. Konečně jste celý svůj text od těchto "zlobivých" znaků vyčistili. Vaše bežná angličtina už více nebude másti jejich stupidní softwér. Super, zmáčknout "Odeslat" a....

...AND... !!!ONLY NOW!!! they bother to inform you that your contribution to their business is actually TOO LONG! Nope, they could not tell you that in the first place. They first had to make you read the LONG VERSION of what you wrote several more times (or do a character search and then manually replace) before they tell you that four fifths of what you just wrote and the 40 minutes it took to write it you just completely wasted. You sucker. Ha Haaa! It's like a very bad joke. An intentional joke made at your expense.

...A... !!!TEPRVE TEĎ!!! se vám uráčí sdělit že váš příspěvek jejich byznysu je PŘÍLIŠ DLOUHÝ! Ne né, nemohli vám to říci třeba jako první. Museli vás první donutit číst znovu několikrát tu DLOUHOU VERZI vašeho příspěvku a odstranit z ní jinak běžně používané znaky, než vám laskavě sdělí, že čtyři pětiny z toho, co jste jim napsali a s čím jste strávili 40 minut vašeho času, jste naprosto promarnili. "Ha háá, naletěli jste, vy hlupáčkové." Je to jako hodně špatný vtip. Záměrný vtip udělaný na váš úkor.

So here we are getting to the point of this whole section.

Takže tady se dostáváme k důvodu, proč píšu celou tuhle kapitolku.

Can anyone get a simple thing so wrong so many times on a single page? A very stupid person maybe. Or maybe it really was a dog who wrote this. Who knows, one can only guess. And those kind of "beings" seem to be hired as programmers by Citibank apparently.They could at least start with a very simple logic and FIRST tell you that your feedback is too long AND ONLY THEN force you to manually fix for them what they could have very easily be fixing in code without the need to even ever having to tell you about it. (Further below I'll show you how easy it is to solve even this problem and stop bothering users with it.)

Je vůbec možné, aby někdo dokázal takhle strašlivě zpackat tak mnoho základních logických věcí na jedné jediné jednoduché web stránce? Hodně hloupý člověk možná. Nebo to opravdu byl pes, kdo tuhle stránku napsal. Kdo ví, můžu zde jen hádat. A tenhle typ "bytostí" se zdá, že je najímán jako programátoři Citi bankou. Minimálně by ten "programátor" mohl začít tou nejjednodušší logikou a PRVNÍ vám říci, že to, co jste jim napsali je pro ně příliš dlouhé A TEPRVE POTÉ vás nutit k manuálním "opravám" vašeho textu, který mohli opravit sami v programu velmi jedoduše bez toho, aby vám vůbec o tom museli kdy říkat. (Níže v textu předvedu, jak jednoduše lze vyřešit super krátkým kouskem kódu i toto a neotravovat tím uživatele.)

OK, so now I know that you are using some very old systems at the backend and hiring dogs to write your front-end code. Great. Next time maybe just tell me in plain English above the form and I won't bother. You obviously don't care for any feedback anyway so letting someone spend - during the years, many hours on it is very nasty indeed.

Fajn, takže teď vím, že používáte nějaké velmi staré systémy na vaší straně a najímáte psy na psaní kódu na straně uživatele. Super. Možná mi příště o vašich vlastních problémech řekněte v jednoduché Angličtině nad daným formulářem a já se ani nebudu snažit. Vás má zpětná vazba zjevně beztak nezajímá, takže nechat mě trávit, v průbehu těch let, mnoho hodin snahou vám pomoci je opravdu velice... jak to mám říci... HNUSNÉ.

Funny thing is...no, actually very sad thing is... that it all actually has very simple solutions that literally would take them like 10 minutes to implement. Or maybe just a couple of minutes (for mostly just finding the code to alter) if they just wanted to utilize AN EXISTING!!! HTML parameter on the text box called "maxlength". This stops the user from writing any more than the set number of characters. It is not as helpful as also having a visible character counter, but would save someone like me 40 out of the 45 minutes I spent on writing my feedback for them several times already.

Co je vtipné je... ne, vlastně velmi smutné je... že všechny tyhle problémy mají velmi jednoduchá řešení, která by doslova zabrala nějakých 10 minut je implementovat. Nebo možná dokonce jen pár minut (především strávených k nalezení místa v kódu, které je nutné upravit), kdyby chtěli využít JIŽ EXISTUJÍCÍHO!!! HTML parametru textového pole jménem "maxlength". Tento parametr zastaví uživatele, jakmile ten napíše maximální povolené množství znaků. Není to řešení tak dobré, jako mít na stránce viditelný ukazatel počtu napsaných znaků, ale ušetřilo by to někomu, jako jsem já, 40 z těch 45ti munut, které jsem už několikrát strávil marně psaním mé zpětné vazby pro ně.

So I'd end up shortening my comprehensive feedback to just one major issue described very shortly and send it. Then I would open the page again and write them feedback about the very feedback page I'm writing to them from, about the "maxlength" parameter and about a possible helpful counter if they felt very generous towards their online users.

Takže výsledek je, že zkrátím můj podrobný popis všech problémů a řešení na jen jeden nejhlavnější problém popsaný velice krátce a pošlu jim to. Pak otevřu tu stránku znovu a pošlu jim zpětnou vazbu přesně na tu stránku, ze které jim to posílám. Píšu jim o tom parametru "maxlength", který mohou využít a vylpešení by to bylo znatelné za cca minutu práce. Také zmíním to možné počítatlo znaků, které by to vylepšilo ještě více, kdyby se náhodou chtěli cítit štědří vůči jejich uživatelům.

And do you think they fixed it in the past like 17 (yes, SEVENTEEN!) years since I first reported this and several times in the years after? Nope! Apparently an unsolvable problem for their programmers and the mountains of money they have that they could throw at the "problem".

Myslíte že tohle spravili za posledních 17 (ano, SEDMNÁCT!) let od té doby, co jsem jim tenhle problém poprvé nahlásíl a poté znovu několikrát? Ne né. Zřejmě tenhle probém zůstává pro jejich programátory a jejich hory peněz, které na řešení tohoto "problému" mohou nasadit, i nadále neřešitelným. A to i přesto, že jsem jim to řešení poslal.

Do you think they ever wrote back? Of course not! But "funnily" enough, they sent me a customer satisfaction survey about two months after I've last reminded myself about how stupid I was to even bother with them. It asked me to please let them know if I may have any problems with their service. Oh no no no, you are not gonna make a sucker out of me again so shortly after you managed to do that the last time just two months back. You don't have to pretend in front of me that you care anymore, dear Citibank.

Myslíte si, že mi kdy napsali zpátky? Samozřejmě, že ne! Ale celkem "vtipně" mi poslali formulář na průzkum zákaznické spokojenosti zhruba dva měsíce poté, co jsem sám sobě naposledy svou snahou připomenul, jak jsem byl hloupý se s nimi vůbec zabývat. Ten průzkum mě prosil, abych jim dal vědět, kdybych měl s jejich službami jakýkoliv problém. Ne ne ne ne nééé, debila ze mě znovu neuděláte tak krátce poté, co se vám to znovu povedlo před pouhými dvěmi měsíci. Už přede mnou nemusíte předstírat, že vám na nás zákaznících jakkoliv záleží, drahá Citi banko.

Aaaanyway...

No nechám už stěžování...

So now, that you have a good general idea about what the problem is and that it could be easily solved in code I'll present the complete solution to both of their insurmountable problems for you right here. As I mentioned before, you do not need to understand it. Just look at how little code would need to be written to solve both.

Takže teď, kdy už máte dobré obecné povědomí o tomto problému a také víte, že může být jednoduše vyřešen v kódu, představím vám kompletní řešení obou těch "nepřekonatelných" problémů přímo zde. Jak už jsem zmínil dříve, nemusíte tomu řešení samotnému technicky rozumět. Jen se koukněte, jak málo programovacího kódu je nutné napsat k vyřešení obou.

The "great problem" of unacceptable characters

Ten "ohromný problém" neakceptovatelných znaků

First let me say that I do actually understand that such big and old institutions like Citibank are likely to still be using some very, very ancient server-side software that is not easy to just replace from one day to another. Some of these old systems can really crash if they receive a single character that they cannot deal with. Fine. First of all though, a customer feedback system should not be such a problem to replace as it could be to replace the actual banking systems - you know, those that deal with money transfers and stuff. Anyway, let us suppose that it is a problem. Well in that case here is your solution Citibank:

První mi dovolte ještě říci, že ve skutečnosti rozumím, že tak velké a staré finanční instituce jako je Citibank v mnoha případech ještě stále používají velice, velice starobylý serverový softwér, který není úplně jednoduché jen tak vyměnit ze dne na den. Některé z těchto staroučkých systémů mohou opravdu zkolabovat, když obdrží i třeba jen jeden jediný znak, se kterým se neumí vypořádat. OK, ale zákaznický formulář pro zpětnou vazbu by neměl být a také není žádným tak velkým problémem vyměnit, či vylepšit, jako je skutečný bankovní systém, který nakládá s převody peněz atd. V každém případě pojďme předpokládat, že to problém je. V tom případě zde je jeho řešení, Citi banko:

Simply have some JavaScript code* that will take what the user writes and even before ever sending it to your servers to process and store just have it remove the characters that your back-end system does not like. (Or maybe discard right away in your case. You do not care anyway, right Citibank? :-) )

Jednoduše mějte na té webstránce nějaký JavaScript kód*, který vezme to co uživatel napíše a než je to odesláno vašim starobylým serverům k vyřízeni první automaticky odstraní ty znaky, které vaše servery "nemají rádi".

*Javascript code is, dear reader, code running in your very browser when you are viewing pages on the Internet, that is written by the developer of that page. It is running on this very page you are reading and nowadays in fact also on the absolute majority of other pages, including internet banking software.
*Javascript kód je, milý čtenáři, kód který je vykonáván přímo ve vašem Internetovém prohlížeči když si prohlížíte web stránky na Internetu. Tento kód je napsán vývojářem / programátorem té dané stránky, aby ta se jevila nějak inteligentní a nefungovala pouze jako novinový článek plný neinteraktivního textu. JavaScript beží i přímo na této stránce, kterou právě čteš a i na absolutní většině ostatních, včetně stránek Internetového bankovnictví, které by se bez JavaScriptu vůbec neobešli.

This is how the whole code that fixes this issue would look like:

Takhle by vypadal kód, který by spravil celé toto nešťastné chování pro Citibank:

function Sanitize(text) {
    var forbiddenChars = ["'", "<", ">", "=", "*"],
        len = forbiddenChars.length,
        char;
    for (var i = 0; i < len; i++) {
        char = forbiddenChars[i];
        text = text.replace(char, "&" + char.charCodeAt(0) + ";")
    }
    return text;
}

THAT'S IT! Really! That is all they would need to add to their code to stop bugging everybody with their own problems and wasting their users' time and nerves.

TO JE CELÉ! Fakt! To je vše, co by museli přidat do jejich kódu, aby přestali otravovat každého uživatele s jejich vlastními problémy a plýtvat uživatelovým časem a nervy.

It could actually be written even much shorter than that on a single line if we surrendered easy human readability. All the computer cares about could be written like this too:

Mohlo by to celé být dokonce napsáno ještě kratšeji, pokud by nám nevadilo obětovat čitelnost. Celé, co počítač zajímá by tedy mohlo být napsáno i takto:

function s(t){var f=["'","<",">","=","*"],l=f.length,c,i=0;for(i;i<l;i++){
c=f[i];t=t.replace(c,"&"+c.charCodeAt(0)+";")}return t;}
I broke the line to two just so that it fits well on this page.
Rozdělil jsem ten řádek na dva jen proto, aby se lépe vešel do formátu této stránky.

Yes, they would also have to call it from their existing code which looks like this:

Ano, museli by také přidat zavolání této funkce z jejich existujícího kódu, což by vypadalo takhle:

text = Sanitize(text);

That does not seem too hard, does it? Well, it really is not.

To nevypadá tak těžké, ne? No opravdu není.

And I also don't remember exactly what characters they do not allow so the list in "forbiddenChars" could be slightly different and possibly slightly longer and if it included the & or semicolon they would have to replace them in the line that uses them, but that's really all.

Také si nepamatuji přesně, které znaky jsou na té jejich stránce zakázané, takže ten seznam ve "forbiddenChars" (řádek č.2 v kódu) by mohl být trošku odlišný od toho mého a třeba mírně delší. A kdyby ten seznam obsahoval &, nebo středník, museli by tyto vyměnit v tom řádku, který je používá, ale to už je opravdu vše.

DONE!

VYŘEŠENO!

Now the "problem" with text being too long...

Tak a teď k tomu problému kdy je zadaný text příliš dlouhý...

I've already mentioned the simplest solution: Somewhere in the page's HTML code, they already have a tag like this

To nejjednodušší řešení jsem již zmínil: Někde v HTML kódu té stránky už mají takovýto "tag"

<textarea id="userFeedback"></textarea>

All they would have to do to fix the biggest problem of letting users happily waste their time writing past the maximum length they will accept would be this:

Celé, co by museli udělat aby spravili ten největší problém nechávání uživatelů vesele plýtvat jejich časem psaním za maximální mez povoleného počtu znaků by bylo toto:

<textarea id="userFeedback" maxlength="300"></textarea>

The only change there is the added "maxlength="300"". And again, that's it! So where are we now at altogether? 10 + 1 lines of code and a little change in one more line. 12 and all major problems are fixed! Or would be if they cared at all.

Jediná změna v tom je přidaný parametr "maxlength="300"". A znovu, to je celé! Takže kde jsme teď všeho všudy? 10 + 1 řádků kódu a malá změna v jednom dalším řádku. 12 a všechny hlavní problémy jsou vyřešené, opravené! Nebo "byly by", kdyby je to sebeméně tížilo. Bohužel netíží.

Improving on the above a little bit

Malá vylepšení dosud udělaných úprav

Stopping me abruptly from writing any more feedback is a huge improvement compared to letting me waste my time for absolutely no reason, but it would be helpful if they told me to start with how many characters they will accept from me and updated me on how I am doing as I go. That way I can sort my thoughts and try to make my message concise enough, yet still complete if at all possible.

Náhlé zastavení příjmu mého klávesnicového vstupu když píšu firmě zpětnou vazbu je sice ohromné vylepšení v porovnání s původním necháním mě plýtvat vlastním časem, kdy mě nechali psát do aleluja, ale pak mi ukázali dlouhý nos, jak jsem naletěl a nařídili zkrátit napsaný text na pouhé malé procento z toho, co jsem ve skutečnosti napsal, ale bylo by ještě lepší, kdyby mi rovnou na začátku řekli, kolik znaků maximálně akceptují a informovali mě průbežně, jak jsem na tom. Tak bych mohl urovnat své myšlenky a naplánovat co nejlépe to, co jim v tom omezeném místě chci a mohu sdělit.

This is where a little visual character counter comes in. And since that is the very thing I'm about to use as an example of how vastly a basic and advanced versions of the same thing may be apart in terms of programming difficulty and time needed to create it, I end this here and ask you to continue reading the next sections.

Zde je místo, kde vstupuje na scénu malé vizuální počítadlo znaků. A protože takové počítadlo je přesně ta věc, kterou chci použít jako příklad toho, jak propastně se mohou základní a pokročilá verze toho samého lyšit co do náročnosti na obtížnost a časovou náročnost programování potřebného k jejímu vytvoření, ukončím toto tady a doporučím přečíst si následující sekce tohoto článku.

Suffice to say that they could just implement the simple solution as I offer it below and again vastly improve the user experience by doing so. It would not be pretty, but their whole feedback page isn't, so it would feel perfectly at home there and still be immensely helpful.

Už by každopádně mělo být jasné, že Citibank by mohli implementovat jen to jednoduché řešení které bude následovat a pořád by zásadním způsobem vylepšili uživatelskou zkušennost a dojem z jejich stránek. Nebylo by to nikterak krásné, ale to není celá ta jejich stránka na uživatelskou zpětnou vazbu, takže by se tento malý dodatek na té stránce cítil i tak jako doma a pořád by z uživatelského hlediska tu stránku výrazně vylepšil.

The simplest solution

To nejjednodušší řešení

Again, you don't have to understand the following code at all. Just look at how long (or short) it is.

Zde znovu podotýkám, že nemusíte rozumět samotnému následujícímu kódu. Jen si všiměte, jak je krátký.

<textarea id="feedback" maxlength="500" onkeyup="Count(this)"></textarea>
<div class="counter"></div>
function Count(el) {
    $(el).next(".counter").text(el.value.length + " / " + el.maxlength);
}

And that my lady or sir is (almost) the smallest amount of code needed to solve the two related problems of:

A to, má milá dámo nebo pane je (téměř) nejmenší množství kódu potřebného k vyřesení těch dvou propojených problémů:

  1. Limiting user input in a text box to a maximum number of characters.
  2. And helping the user to know at the same time what the maximum is and how many characters they've written so far.
  1. Omezení maximálního množství znaků, které je možné napsat do textového pole formuláře.
  2. A zároveň pomoc uživateli tím, že mu dáme vědět, jaké to maximum je a kolik znaků dosud napsal.

Programming is easy and fast right? Well, hold your horses for a second...

Programování je jednoduché a ryhlé, že? No pozor, držte své koně na uzdě...

Even the above would need to have some associated styling* information so that it is positioned nicely and logically on the page and looks all right.

I tyto výše uvedené příklady by v reálném světě potřebovali související formátovací informace zvané "styly*", aby byly na stránce umístěny logicky a přehledně a vypadali, jako že tam patří.

Also, the code above does not solve all the possible interactions with the textbox and will not update the counter under all circumstances, so more code may need to be written even here. But it solves about a 99% big chunk of the desired basic behaviour.

Navíc ten kód výše neřeší všechny možné způsoby interakce s tím textovým polem and nebude aktualizovat to počítadlo za všech podmínek, takže i v tomto případě by bylo ve skutečnosti potřeba napsat o něco více kódu. Ale ten kód výše řeší zhruba 99% podíl požadovaného chování. Často právě to jedno zbývající procento zabere více času, než ta hlavní část.

* Styling is information about how a component should look visually on the page, how it should be positioned, how it should visually react to events like being clicked and thus receiving the active blinking cursor and many others as needed. An example for our text field and counter would look something like this:
* Styly jsou informace pro počítač o tom, jak má komponent vypadat vizuálně na stránce, kde má být umístěn, jak by měl vizuálně reagovat na události jako je kliknutí a tím obdržení tzv. "fokusu", kdy se daný prvek stává tím aktivním, zvoleným na stránce a třeba v případě textových polí v něm začne blikat kurzor. Styly dále definují mnohé další vizuální definice podle potřeby. Příklad pro naše textové pole a počítadlo znaků by vypadal nějak takto:
<style>
    textarea, input[type=text] {
        margin-top: 9px;
        background: #cfe8de;
        color: black;
        border: solid 2px #2b2d43;
        border-radius: 6px;
        display: block;
        width: 100%;
        padding: 10px 15px;
        font-size: 14px;
        line-height: 1.42857143;
        box-shadow: 5px 5px 9px 0 rgba(56, 51, 51, 0.5);
    }
    textarea:focus, input:focus {
        background: #d5e8cf;
        border:solid 3px rgb(89, 187, 69);
    }
    #feedback {
        height: 80px;
    }
    .counter {
        text-align: right;
        font-size: 0.8em;
        font-weight: bold;
    }
</style>

And again, it could be (and usually is) a bit longer than that to also describe how the look changes not only when the box is focussed (that is when it is the active element in which the cursor is blinking) as done above in line number 20, but also when the mouse hovers over it, when it is active (which is similar to focus but yet happens under different conditions) etc.

A znovu, mohlo by tohle být (a většinou také je) trošku delší než náš příklad aby to pole vědělo, jak má vypadat ne pouze když má fokus, tak jak je to definováno výše v řádku 20, ale také když se nad tím polem objeví kurzor myši, když je to pole aktiví (což je podobné fokusu, ale stává se za rozdílných podmínek) atd.

The funny thing is that often, to also handle those less likely but possible types of user interaction and events happening on the page such a textbox is on - that is the remaining 1% of possible types of interactions - the programmer ends up having to write significantly more additional code than it is in the above example. Just how much that can easily be you'll see shortly in "The more complete solution".

Vtipné je, že často k tomu, abychom obsloužili tyto méně časté ale možné typy iterakce uživatelů a událostí stávajících se na stránce, na které se takové textové pole nachází - tedy to zbývající 1% možných typů interakcí - musí programátor napsat podstatně více přídavného kódu než je uvedeno v příkladu výše. O kolik více to klidně může být uvidíte za krátko pod hlavičkou "Kompletnější řešení".

But first, here is the result of the above code.

Ale ze všeho nejdříve, zde je výsledek toho kódu výše.

Demo

To test how the code above works just click the green-coloured textbox below and start typing. You will notice a counter appearing on the right below it as soon as you type the first character.

K otestování, jak ten kód výše funguje klikněte na zelené textové pole níže a začněte psát. Všimněte si počítadla objevujícího se napravo pod ním jakmile napíšete první znak.

Alternatively, you can just click the "SIMULATE" button below it to start an automatic demonstration.

Případně můžete jen kliknout na tlačítko "SIMULACE" pod textovým polem k odstartování automatické demonstrace.

Note how once the counter appears it never disappears again even if you delete all text from the text box again. That may be actually the desired behaviour in this case. But in case the desired behaviour was that the counter was supposed to disappear again, testing for the condition of 0 entered characters and hiding the counter again would require a few more lines of code. And that is how the amount of code that is needed grows - with every additional requirement. Here though it would be OK for it to stay there once shown. It would be a perfectly good solution for the problem described earlier that Citibank has had for the past at least 17 years on their feedback page.

Všimněte si, že jakmile se počítadlo jednou objeví už nikdy znovu nezmizí, ani když znovu všechny znaky z toho textového pole vymažete. To může být přesně to požadované chování v tomto případě. Ale v případě, že požadované chování by bylo aby to počítadlo zase zmizelo, testování té podmínky 0 znaků a opětovné skrytí toho počítadla by vyžadovalo dalších pár řádků kódu. A to je přesně jak množství kódu, který je nutné napsat, postupně roste, s každým dalším požadavkem. Zde ale by bylo OK, aby to počítadlo zůstalo, kde je, když už se objeví. A bylo by to perfektně dobré řešení i pro tu strašnou stránku zpětné zákaznické vazby Citibanku, která je stejně hrozná už minimálně 17 let, co jsem jejich klientem.

But for examle in the application I am writing currently and for which I programmed similar character counter component and code for which is going to follow, I need the counter to be shown only after a certain number of characters have been written by the user and it should also disappear when the number of characters goes below a defined threshold and also after the field looses focus (stops beeing an active field). And that requires more and more code.

Ale třeba v aplikaci, kterou právě píši a kvůli které jsem naprogramoval komponentu podobného počítadla znaků, jejíž celý kód bude dále následovat, je potřeba, aby se to počítadlo ukazovalo jen když je v tom poli určitý počet znaků (tedy ne 1 a více, ale definovatelná hranice, nad kteoru se počítadlo objeví) a aby zmizelo v každém případě jakmile dané pole stratí fokus (tedy přestane být aktivním, tedy zvoleným, polem). A to už zase vyžaduje další a další kód.

The more complete solution

Kompletnější řešení

The following solution for the same problem as solved by the "Simple solution" will be more complicated. In fact, it will be vastly more complicated. But it will also be vastly more capable and "complete". It is done in a way to make a reusable component from the defined behaviours and visual presentation. This component accepts parameters to very easily define both its behaviour and visual presentation.

Následující řešení stejného problému, jaký byl vyřešen v sekci "To nejjednodušší řešení" bude komplikovanější. Tedy ve skutečnosti bude propastně více komplikované. Ale také bude zároveň propastně více schopné reagovat jednoduše na komplexní požadavky jeho chování a bude tím značně "kompletnější". Toto řešení vyúsťuje v komponentu, která je jednoduše využitelná kdekoliv jinde nejen v mé, ale jakékoliv jiné aplikaci. Komponenty se pak vyznačují tím, že jak jejich chování, tak vizuální prezentace jsou jednoduše nastavitelné.

When you click the below title you'll be shown the complete code. As I pointed out before twice already, you do not need to understand it. Just look at its size compared to the simple solution to the same problem, which had in total 32 lines of code.

Až kliknete na titulek níže, odhalí se vám kompletní kód této mnou naprogramované komponenty. Jak jsem předeslal už dvakrát v dřívějším textu, nemusíte tomu kódu rozumět. Jen koukněte na jeho množství a porovnejte ho s tím jednoduchým řešením stejného problému výše, které mělo všeho všudy 32 řádků kódu.

A spoiler: this one has 577 lines of code.

Prozradím předem, že toto řešení má 577 řádků kódu!

The complete code

Kompletní kód

The code consists of two parts:

Tento kód se zkládá z dvou částí:

  1. The code that makes the component to have the desired behaviour.
  2. The code that defines the component's visual appearance including for all of its different states.
  1. Kód, který zařizuje požadované chování této komponenty.
  2. Kód, který definuje vzhled této komponenty, tedy to, jak se tato komponenta prezentuje na stránce, která jí využívá.

1. Example: The behaviour code1. Příklad, část 1: Kód definující chování

(function (win) {
    function Dummy() { }

    var m_oCallbacks = {
        countChanged: Dummy,
        stateChanged: Dummy,
        shown: Dummy,
        basic: Dummy,
        highlighted: Dummy,
        warn: Dummy,
        max: Dummy,
        exceeded: Dummy
    },
        m_oPrevData = {},
        _;

    win.CharCounter = _ = {
        /**
            * This initializes the component, either binding all text boxes and text areas with a class of "counter" directly to each such element's event handlers OR INSTEAD using a global event handler registration function which causes any text box or textarea element that has the "counter" class to be called.
            * @param {any} globalEventHandlerRegistrationFunction An optional global event registration function, if you have one, with a signature of (eventType, handlerFunction, elementClass) (where elementClass is the class a component must have if the handler is to be called for it) and which calls the handlers with (event) as the signature.
            * @param {any} eventCallbacks An optional object containing one or more of the following properties: {
            *      countChanged (el, data)
            *      stateChanged (el, data)
            *      shown (el, data)
            *      basic (el, data) // Basic (non-highlighted) version of the counter is shown or hidden.
            *      highlighted (el, data)
            *      warn (el, data)
            *      max (el, data)
            *      exceeded (el, data)
            *    }
            *    where those should contain callback functions the user of the component wants to be called when.. characterCount is changed, or counter is shown, highlighted, changes to warn or max states or when the count is exceeded.
            */
        Init: function (globalEventHandlerRegistrationFunction, eventCallbacks) {
            if (globalEventHandlerRegistrationFunction) {
                var cls = "counter";
                globalEventHandlerRegistrationFunction("keyup", onKeyUp, cls);
                globalEventHandlerRegistrationFunction("focusin", onFocusIn, cls);
                globalEventHandlerRegistrationFunction("focusout", onFocusOut, cls);
            } else {
                $("input.counter")
                    .not(".counter-init")
                    .addClass("counter-init")
                    .keyup(ShowCounter)
                    .focus(ReShowCounter)
                    .blur(HideCounter);
                $("textarea.counter")
                    .not(".counter-init")
                    .addClass("counter-init")
                    .keyup(ShowCounter)
                    .focus(ReShowCounter)
                    .blur(HideCounter);
            }

            AssignCallbacks(eventCallbacks);
        },

        StateEnum: {
            notInitialized: 0,
            notShown: 1,
            basic: 2,
            highlighted: 3,
            warn: 4,
            max: 5,
            exceeded: 6
        },

        AssignEventCallback: function (eventName, callbackFunction) {
            if (eventName) {
                var obj = {};
                obj[eventName] = callbackFunction;
                AssignEventCallbacks(obj);
            }
        },

        Refresh: function (element) {
            if (typeof element == "string" && element.length && element.substr(0, 1) != "#")
                element = "#" + element;
            element = $(element || $("body"));
            var tag = element[0].tagName.toUpperCase(),
                list;

            if (((tag == "INPUT" && element.attr("type") == "text") || tag == "TEXTAREA") && element.hasClass("counter"))
                list = [element];
            else
                list = element.find("input[type=text].counter,textarea.counter");

            if (list.length) {
                $.each(list, function () {
                    var el = $(this);
                    ShowCounter(el);
                    if (!el.is(":focus"))
                        HideCounter(el, true);
                });
            }
        }
    };

    $.fn.extend({
        hasClasses: function (selectors) {
            var self = this;
            for (var i in selectors) {
                if ($(self).hasClass(selectors[i]))
                    return true;
            }
            return false;
        }
    });

    function onKeyUp(ev, el, id, tgt) {
        ShowCounter(el);
    }

    function onFocusIn(ev, el, id, tgt) {
        ReShowCounter(el);
    }

    function onFocusOut(ev, el, id, tgt) {
        HideCounter(el);
    }

    function AssignCallbacks(defObj) {
        if (defObj) {
            var obj = m_oCallbacks,
                p;
            for (p in defObj) {
                if (typeof obj[p] == "function")
                    obj[p] = defObj[p] || Dummy;
            }
        }
    }

    function HideCounter(el, skipEvent) {
        var data = el.data("counter-data"),
            cbk;
        if (data && data.isShown && (
            (!data.isTextArea && !el.hasClass("counter-keep-on-blur"))) ||
            (data.isTextArea && el.hasClass("counter-hide-on-blur"))) {
            el.css("padding-right", data.origPadding);
            el.next().hide();
            data.hidden = true;
            cbk = m_oCallbacks;
            if (cbk.shown && !skipEvent)
                cbk.shown(el, data);
        }
    }

    function ReShowCounter(el) {
        var data = el.data("counter-data"),
            counter = el.next(),
            cbk;
        if (data) {
            ShowCounter(el);
            el.css("padding-right", data.padding);
            if (counter.length)
                counter[0].style.removeProperty("display");
            data.hidden = false;
            if (data.isShown && (!data.isTextArea || el.hasClass("counter-hide-on-blur"))) {
                cbk = m_oCallbacks;
                if (cbk.shown)
                    cbk.shown(el, data);
            }
        }
    }

    function ShowCounter(el) {
        var counter = el.next(),
            origData = el.data("counter-data"),
            prev = PrevData(origData),
            data = CharThresholds(el, origData),
            count,
            divs,
            parent;

        if (!counter.length) {
            parent = el.parent();
            if (parent.css("position") == "static")
                parent.css("position", "relative");
            counter = $("
" + data.maxAt + "
"); if (data.isTextArea) counter.addClass("counter-area"); el.after(counter); count = counter.find(">div"); divs = count.find("div"); if (!data.isTextArea && el.hasClass("counter-vertical")) { counter.addClass("counter-vertical"); divs.css("display", "block"); } else { divs.css("display", "inline-block"); } } else { count = counter.find(">div"); divs = count.find("div"); } var v = el.val(), len = v.length, hasMax = data.maxAt != 0, hasWarn = data.warnAt > -1, hasHl = data.highlightAt > -1, hasBasic = data.basicAt > -1, add = data.stopAtMax ? 0 : 1, showMax = hasMax && len >= (data.maxAt + add), showWarn = !showMax && hasWarn && len < (data.maxAt + add) && len >= data.warnAt, showHl = !showMax && !showWarn && hasHl && ((hasWarn && len < data.warnAt) || (!hasWarn && hasMax && len < (data.maxAt + add))) && len >= data.highlightAt, showBasic = !showMax && !showWarn && !showHl && hasBasic && ((hasHl && len < data.highlightAt) || (!hasHl && hasWarn && len < data.warnAt) || (!hasHl && !hasWarn && hasMax && len < (data.maxAt + add))) && len >= data.basicAt, classes = [data.basicClass, data.hlClass, data.warnClass, data.maxClass], wasShown = counter.hasClasses(classes), showAny = showBasic || showHl || showWarn || showMax, visibilityChange = showAny != wasShown, states = _.StateEnum, state; if (data.remainingCount < 0) state = states.exceeded; else if (showMax) state = states.max; else if (showWarn) state = states.warn; else if (showHl) state = states.highlighted; else if (showBasic) state = states.basic; else state = states.notShown; data.curState = state; if (!data.maxAt || data.maxAt < 0) return; // Do not show the counter as the required max length has net been defined. data.isShown = showAny; data.isBasic = showBasic; data.isHighlighted = showHl; data.isWarn = showWarn; data.isMax = showMax; data.prevCount = prev.count; data.prevRemainingCount = prev.remaining; data.prevState = prev.state; ExecuteCallbacks(el, prev, data, showAny, showBasic, showHl, showWarn, showMax); divs.eq(0).text(data.curCount); if (!data.isTextArea || !visibilityChange || showAny) { if (data.isTextArea && showAny) counter.slideDown(); counter .toggleClass(data.maxClass, showMax) .toggleClass(data.warnClass, showWarn) .toggleClass(data.hlClass, showHl) .toggleClass(data.basicClass, showBasic); } if (data.isTextArea) { if (wasShown != showAny) { if (showAny) counter.hide().slideDown(); else counter.slideUp(function () { counter.removeClass(classes); }); } } if (!data.isTextArea) { data.padding = counter.width() + 20; el.css("padding-right", data.padding); } } function CharThresholds(el, data) { var len = el.val().length; if (!data) { var def = el.data("counter-def"), vals, max = el.attr("maxlength"), isTextArea = el[0].nodeName.toUpperCase() == "TEXTAREA"; if (max) max *= 1; if (def) vals = def.split(","); else vals = []; data = { id: el.attr("id"), basicAt: CalcThreshold(max, vals, 0, isTextArea ? "50%" : "x"), highlightAt: CalcThreshold(max, vals, 1, "80%"), warnAt: CalcThreshold(max, vals, 2, isTextArea ? "90%" : -5), maxAt: CalcThreshold(max, vals, 3, max), stopAtMax: max || (vals && vals.length && vals.length > 3 && vals[3] && vals[3] > 0), basicClass: CounterClass(vals, 4, "counter-basic"), hlClass: CounterClass(vals, 5, "counter-hl"), warnClass: CounterClass(vals, 6, "counter-warn"), maxClass: CounterClass(vals, 7, "counter-max"), isTextArea: isTextArea, origPadding: el.css("padding-right"), prevCount: -1, prevRemainingCount: 0, remainingCount: 0, prevState: _.StateEnum.notInitialized, curState: _.StateEnum.notInitialized, isShown: false, isBasic: false, isHighlighted: false, isWarn: false, isMax: false }; el.data("counter-data", data); if (data.stopAtMax) el.attr("maxlength", data.maxAt); } // The following check is here for when the box is filled programmatically, which ignores the "maxlength" setting. if (data.maxAt > 0 && len > data.maxAt && data.stopAtMax) { len = data.maxAt; el.val(el.val().substr(0, len)); } data.curCount = len; data.remainingCount = data.maxAt - len; data.isExceeded = data.remainingCount >= 0; return data; } function CounterClass(vals, idx, defaultVal) { return vals && vals.length > idx && vals[idx].trim().length ? vals[idx].trim() : defaultVal; } function CalcThreshold(max, vals, idx, defaultVal) { if (!vals) return (defaultVal * 1) || -1; else { if (vals.length < (idx + 1) || !vals[idx].length) vals[idx] = defaultVal + ""; var val = vals[idx].trim(), neg = false, m = max || Math.abs(!isNaN(vals[3]) ? vals[3] * 1 : 0); if (val.substr(0, 1) == "-") { neg = true; val = val.substr(1); } if (val.substr(val.length - 1) == "%") { val = val.substr(0, val.length - 1); if (isNaN(val)) return -1; if (m > 0) { val = Math.round(m * (val / 100)); } else { return -1; } } if (isNaN(val)) return -1; if (neg && idx < 3) { // idx == 0 means that val is the max value which, if negative, just means that it is a hard stop and we still return the absolute value of if (m > 0) return m - val; else return -1; } else return val * 1; } } function PrevData(data) { var prev = m_oPrevData; if (data) { prev.count = data.curCount; prev.remaining = data.remainingCount; prev.state = data.curState; prev.shown = data.isShown; prev.basic = data.isBasic; prev.hl = data.isHighlighted; prev.warn = data.isWarn; prev.max = data.isMax; prev.exceeded = data.isExceeded; } else { prev.count = -1; prev.remaining = -1; prev.state = _.StateEnum.notInitialized; prev.shown = false; prev.basic = false; prev.hl = false; prev.warn = false; prev.max = false; prev.exceeded = false; } return prev; } function ExecuteCallbacks(el, prev, cur, showAny, showBasic, showHl, showWarn, showMax) { var cbk = m_oCallbacks, count = prev.count != cur.curCount, state = prev.state != cur.curState, shown = showAny != (prev.basic || prev.hl || prev.warn || prev.max), basic = prev.basic != showBasic, hl = prev.hl != showHl, warn = prev.warn != showWarn, max = prev.max != showMax, exceeded = prev.exceeded != cur.isExceeded; if (count) cbk.countChanged(el, cur); if (state) cbk.stateChanged(el, cur); if (shown) cbk.shown(el, cur); if (basic) cbk.basic(el, cur); if (hl) cbk.highlighted(el, cur); if (warn) cbk.warn(el, cur); if (max) cbk.warn(el, cur); if (exceeded) cbk.exceeded(el, cur); } })(window);

2. Example: The visual appearance code2. Příklad, část 2: Kód definující vzhled

.counter-container {
    &::after {
        content: "";
        clear: both;
        display: table;
    }
}

div.counter {
    background: transparent;
    border-top-right-radius: 3px;
    border-bottom-right-radius: 3px;
    position: absolute;
    padding: 5px;
    top: 2px;
    bottom: 2px;
    right: 2px;
    font-size: 0.7em;
    font-family: "Courier New", Courier, monospace;
    display: none;
    align-items: center;
    justify-content: center;

    > div {
        > div {
            &:nth-child(2) {
                /* To display a slash as the divider. */
                &::before {
                    content: "/";
                }
            }
        }
    }

    &.counter-basic,
    &.counter-hl,
    &.counter-warn,
    &.counter-max {
        display: flex;
        transition: background-color 100ms ease-in, color 50ms ease-in 50ms;
    }

    &.counter-area {
        position: relative;
        float: left;
        justify-content: flex-end;
        align-items: center;
        border-top-right-radius: 0px;
        border-top-left-radius: 0px;
        border-bottom-left-radius: 3px;
        border-bottom-right-radius: 3px;
        padding: 1px 5px;
        top: -2px;
        right: inherit;
        left: 2px;
        box-shadow: inset 2px 9px 4px -7px rgba(0,0,0,0.2);

        &.counter-basic,
        &.counter-hl,
        &.counter-warn,
        &.counter-max {
            transition: background-color 900ms ease-in, color 200ms ease-in 200ms;
        }
    
        > div {
            > div {
                &:nth-child(1) {
                    /* To display a slash as the divider. */
                    &::before {
                        content: "Typed ";
                        padding-right: 0.5em;
                    }
                }

                &:nth-child(2) {
                    /* To display a slash as the divider. */
                    &::before {
                        content: "of max.";
                        padding: 0 0.5em;
                    }
                }

                &:nth-child(3) {
                    /* To display text after max. */
                    &::after {
                        content: "characters.";
                        padding-left: 0.5em;
                    }
                }
            }
        }
    }

    &.counter-vertical {
        > div {
            > div {
                width: 100%;
                text-align: right;

                &:nth-child(2) {
                    /* To display a vertical divider */
                    width: 100%;
                    margin: 0 auto;
                    border-top: solid 1px;
                    margin-bottom: 1px;

                    /* To display a slash as the divider. */
                    &::before {
                        content: "";
                    }
                }
            }
        }
    }

    &.counter-basic {
        background: transparent;
        color: #999;
    }

    &.counter-area {
        &.counter-basic {
            background: white;
            color: inherit;
        }
    }

    &.counter-eager {
        background: lightskyblue;
    }

    &.counter-hl {
        background: yellow;
    }

    &.counter-warn {
        background: #ffa443;
        //color:white;
    }

    &.counter-max {
        background: rgb(253, 21, 21);
        color: white;
        transition: background-color 0ms ease-in, color 0ms ease-in 0ms
    }
}

So that is 18 times more code than then the previous solution with just 32 lines!

Tak to je 18 krát více kódu, než toho v předchozím řešení s pouhými 32 řádky!

Why write so much code if the simple solution was kind of fine? Well, simply, because we want to build better products for our users. We want to sell these products so they must be capable of solving the user's problems and possibly not be annoying while doing it. It also helps, of course, if on top of being helpful and not annoying they also are actually pretty. People like pretty. That's the only purpose of jewelry for example and also the main one behind the fashion industry.

Proč psát o tolik více kódu když to jednoduché řešení bylo celkem dobré? No jednoduše řečeno, protože chceme vytvářet lepší produkty pro naše uživatele. Chceme naše produkty prodávat, takže musí být schopné řešit uživatelovi problémy a pokud možno nebýt u toho iritující. Také samozřejmě pomáhá, když navrch k tomu, že náš produkt uživateli s něčím pomáhá a neirituje, je navíc hezký na pohled. Lidé mají rádi hezké věci. To je jediný důvod, proč se například prodávají šperky a hlavní důvod stojící za celým módním průmyslem. To samé tedy platí i pro softwér.

We also do it because it is the right thing to do. We could write the same few lines of code as in the simple example every time we put a text box on a page. Yes we could. Or we can write something that behaves as a complete component, gives us all the necessary options and offers us different looks automatically so that the next time we need it on a page we just tell the textbox to simply display a counter in this case and tell it very simply how and when to display it and possibly what events to announce to the other elements on the page so that they can react to those events.

Také to děláme protože je to tak správné. Mohli bychom psát stejných pár řádků kódu jako v našem jednoduchém případě vždy, když potřebujeme zobrazit počítadlo znaků. Ano, mohli bychom. Ale také můžeme napsat jednou jedinkrát něco, co se chová jako kompletní komponenta, která nám dává veškerá možná požadovaná chování a vzhledy automaticky, takže až budeme příště potřebovat zobrazit počítadlo znaků, které, když budeme potřebovat, se navíc může chovat a vypadat dle přání, kdy se tato nadefinují jednoduchými parametry, už ho máme a můžeme ho využít znovu a znovu a znovu, s nutností pouze sdělit přídavným parametrem danému textovému poli, že chceme, aby navíc zobrazovalo počítadlo a taky kdy a jak. To vše jen přidáním v tomto případě většinou jediného parametru!

Navíc, tato komponenta umí ohlašovat jakýmkoliv jiným komponentám či ostatnímu kódu, když se stanou určité události, jako například "počet znaků v textovém poli dosáhl 80ti procent maxima". A ty ostatní komponenty mohou na takovou událost v daném programu po svém nějak reagovat. To všechno umí tato komponenta díky tomu množství kódu, který byl potřeba k jejímu vytvoření.

To demonstrate this, using the advanced code presented above, I can only add a data-counter-def attribute to an existing text box like so:

Pro demonstraci, s využitím toho pokročilého kódu výše, mohu pouze přidat data-counter-def atribut k existujícímu textovému poli takto:

<textarea id="feedback" class="counter" data-counter-def="0,400,-30,500"></textarea>

and it will not only display a much nicer looking counter, but also

a to pole mi nejen zobrazí mnohem hezčeji vypadající počítadlo, ale také

  • display 5 different stages of it,
  • will hide and show itself when needed,
  • will stop the user from typing more than the maximum allowed number of characters (500 here),
  • and also announce to the other components on the page when its stages have changed, that the number of characters changed etc. so that they can react to it if and how they need to.
  • It will also work automatically if my other code programatically adds any number of new text boxes to the page
  • and it is now reusable in any other projects I or others may need a nice and capable counter for.
  • zobrazí 5 různých fází,
  • skryje samo sebe když je potřeba,
  • zastaví uživatele když napíše maximální definované množství znaků (500 v našem příkladě),
  • a také oznámí ostatním komponentám v programu, když se jedna fáze změní v druhou, že se změnil počet zadaných znaků atd., takže tento okolní kód může na tyto události reagovat pokud a jak potřebuje
  • Také toto bude fungovat automaticky když jiný kód programaticky* přidá jakékoliv množství nových textových polí na stránku
  • a je též od teď použitelný v jakýchkoliv dalších projektech, kde já nebo jiní potřebují dobře vypadající a schopné počítadlo znaků.
*Programmatically means, that the program itself will add a beforehand unspecified number of text fields (or other components) as opposed to components directly specified by the programmer in code. In other words, we don't know beforehand how many of those components it will be necessary to display to the user so we write the program in such a way that it dynamically reacts to the needs of the situations and adds necessary components as needed.
*Programaticky znamená, že samotný program přidá dříve programátorem konkrétně nespecifikovaný počet textových polí (nebo jiných komponent) na rozdíl od programátorem přesně v programu předem nadefinovaných komponent. Tedy jinak řečeno, předem dopředu nevíme, kolik daných komponent bude nutné zobrazit a proto napíšeme danou část programu tak, aby je doplňoval automaticky dle potřeby.

All that with less code than presented in the simplest example. In fact, all I needed to do was replace the previously mentioned maxlength='500' attribute in the otherwise already existing textarea component with a data-counter-def='0,400,-30,500' attribute by which I have told it both when to stop the user as well as when to show its different visual stages.

Všechno toto s méně kódem než bylo ukázáno v tom nejjednodušším možném příkladu. Celé, co musím udělat je vyměnit dříve popsaný atribut maxlength='500' v tom jinak již existujícím kódu definujícím textové pole textarea tímto atributem: data-counter-def='0,400,-30,500' kterýmžto jsem tomu textovému poli řekl, že má použít mou komponentu počítadla, kdy má uživatele zastavit a také kdy má uživateli ukázat své jednotlivé fáze. Jak je toto najednou jednoduché a ukrutně schopné zároveň.

You should also know

Také byste měli vědět

You should also know that the code above is never written like a book - that is line by line. The code grows as it is being developed not only by adding lines to its end. In fact, most of the time it does not grow by adding lines, including whole new functions, to the end of it. We may create a new function, which can be put almost "anywhere" inside of the code file, including the end, but in the interest of clarity to future maintainers of the code, including oneself, we attempt to bunch related functions in proximity to each other.

Také byste měli vědět, že ten programový kód uvedený výše není nikdy napsán jako kniha, tedy řádek po řádku. Programový kód roste organicky tak, jak je postupně vyvíjen ne jen přidáváním nových řádků na jeho konec. Ve skutečnosti většinou neroste přidáváním nových řádků, včetně celých nových funkcí, na jeho konec. Můžeme vytvořit novou funkci, kterou můžeme napsat téměř "kamkoliv" v rámci souboru, který píšeme, nebo často i přidáváme funkce do jiných souborů, které vzájemě "spolupracují". Proč novou funkci většinou nedáme na konec souboru je z důvodu čitelnosti budoucím udržovatelům tohoto kódu, včetně nás samých. Naším záměrem je udržovat nějakým logickým způsobem spolu související funkce pohromadě.

At the same time as adding a whole new function, we may alter 5 other lines in 5 other, already existing functions, some or all of which may be in different files, delete a couple of lines and replace one of them with three new lines of code etc. And we may need similar changes in 5 other different code files. So the actual amount of code written is (*probably significantly) more than just, in this case, 18 times more between the simple and advanced versions of the code that I presented.

V tu samou chvíli, co přidáváme do programu zcela novou funkci, můžeme pozměnit pět dalších řádků v pěti dalších, už existujících funkcích, existujících i třeba v jiných souborech, vymazat další dva řádky zase někde jinde, vyměnit jeden z nich za pár nových řádků atd. Takže celkové množství napsaného kódu je (*pravděpodobně výrazně) vyšší, než pouze v tomto případě 18ti násobným rozdílem mezi jednoduchou a komplexní verzí kódu, který jsem výše prezentoval.

And that is not even the whole story still. All the code written and all the functions it contains must be logically sound, syntactically perfect and must successfully cooperate with each other to result in exactly what we wanted to achieve. So to write whatever amount of code is the easy part. Coming up with the logic behind it, implementing it by the code and getting it all to work* correctly is the (much) harder and more time-consuming part of software programming.

A to stále ještě není celý příběh. Všechen ten napsaný kód a všechny ty napsané funkce, které obsahuje, musí být logicky konsistentní, syntakticky perfektní a musí spolu navzájem úspěšně kooperovat tak, aby vyústili přesně v takové chování, kterého se snažíme dosáhnout. Tudíž napsání jakéhokoliv množství kódu je ta jednoduchá část. Vymyslení té logiky stojící za tímto kódem, její úspěšné implementování a zajištění toho aby to celé nakonec správně fungovalo* je (mnohem) těžší a časově náročná část programování softéru.

A "finished" code file like the one above has gone through many iterations and have changed significantly since its first working form.

"Dokončený" kódový soubor jako ten prezentovaný výše prošel mnoha a mnoha revizemi a změnil se zásadně od jeho první pracovní formy.

Explanation of some terms:

Vysvětlení výrazů:

* "Getting it all to work": After adding every new feature, we have to go, run it, manually step through* the new code line by line while verifying that the values kept and updated by the program conform to what is actually expected, test any edge cases* and confirm that the program will still not fail even after such conditions. Only after all of that we can sign off on that code as done (for now anyway; until new features turn out to be required or a bug* that we did not catch during this process is discovered).

This phase is called debugging* and usually takes about twice as long as the phase of actually writing the code.

* "Aby to celé správně fungovalo": Po přidání každého nového chování musíme program spustit, dostat se na místo, kde se toto chování má projevit a prokrokovat* ty nově přidáné řádky řádek po řádku, zatímco ověřujeme, že hodnoty uchovávané a měněné touto částí programu souhlasí s těmi, které jsou očekávány. Musíme otestovat všechny mezní případy* a potvrdit, že program nezkolabuje ani v takových podmínkách. Pouze poté můžeme nazvat tento kód jako "hotový" (alespoň pro teď, dokud se neobjeví nové požadavky na funkčnost, nebo dokud se neobjeví "moucha" (anglicky "bug", což v přesném překladu znamená obecně "hmyz")*, kterou jsme při našem testování nenašli).

Tato fáze se nazývá debugging* (tedy "odhmyzování", jestli to mám nějak přeložit do Češtiny) a obyčejně trvá zhruba dvakrát déle, než trvalo vymyslení a napsání celého toho kódu.

* "Edge cases: Conditions that do not happen often or maybe they happen only once ever when the very first user uses the program for the very first time.
* "Mezní případy": To jsou podmínky, které v programu nenastávají často, nebo nastávají po nainstalování programu jen jednou jedinkrát, když se k němu přihlásí úplně první uživatel.

* "A bug" and "debugging": Words that are carried over from the times of the very first computers which were built using electron tubes that were as big as light bulbs, which were also glowing with light when in operation. The light attracted flies, mosquitoes and other bugs which were subsequently fried as the tubes were also running pretty hot. The build-up of bugs often caused bad connections and so the computers needed to be cleaned off the bugs periodically. Thus "debugging".

Nowadays in software, when it has bugs it means that it is not operating as intended due to a logical error the programmer made, or due to a condition that was not initially considered by the programmer, due to a syntax error, where a single missing or superfluous letter, dot, semicolon etc. can prevent the program from running when it reaches a certain point in the code because it renders the code not understandable by the computer or makes the program behave differently than actually intended etc.

You can probably appreciate how difficult it could be to spot a missing semicolon among thousands lines of code... That too, takes time. And logical errors are often much, much more difficult to find.

* "Moucha / bug" a "odhmyzování / debugging": To jsou slova která zůstala z dob úplně prvních počítačů, které byly postaveny s pomocí tzv. "elektronek", tedy elektronických komponent připomínajících světelné žárovky, které sice nesvítili tak jasně, jako žárovky, ale nějaké světlo vydávali a hlavně produkovali také velké množství tepla. Produkované světlo lákalo mouchy, můry, komáry a jiný létající hmiz, který byl následně produkovaným žárem zabit a upečen uvnitř daného elektronického obvodu. To způsobovalo postupné zanesení tímto nevyžádaným materiálem, což následně způsobovalo poruchy celého počítače a tak se tyto staré počítače museli čas od času od tohoto hmyzího materiálu vyčistit. Proto "odhmyzování", neboli "debugging".

V dnešní moderní době, když má softwér "mouchy", máme tím na mysli, že se tento softwér nechová tak, jak bylo zamýšleno, protože obsahuje buď logické chyby, které jeho programátor učinil. Nebo program nefunguje kvůli podmínkám, které v programu nastaly, které programátor dopředu nepředpokládal, nebo kvůli syntaktické chybě, kde jediné chybějící, nebo naopak přebývající písmenko, tečka, středník atd. může zabránit programu v jeho běhu, když se dostane na takové místo v tomto programu, protože to pak takový program činí najednou počítači nesrozumitelným, nebo způsobuje jiné chování programu, než jeho programátor zamýšlel atd. atd.

Snad dokážete ocenit jak těžké může být nalézt chybějící středník mezi tisíci řádky kódu. To také zabírá dost času. A logické chyby se často hledají ještě mnohem a mnohem hůře.

* "Stepping through": When a program is finished and the user runs it on a computer, the program code runs (= is executed by the computer) very, very fast. However, our software development tools allow us (very fortunately indeed) to stop the execution after executing each instruction / line of code and keep it stopped as long as we need to, to let us examine the values of all the variables (= all the program / computer remembers for us) and verify that the logic* we wrote works as intended.

* "Prokrokovat / krokování programem": Když je program dokončen a uživatel ho spustí na počítači, tento program běží (= je vykonáván počítačem) velmi, velmi rychle. Avšak naše vývojové nástroje nám umožňují (což je opravdu velké "štěstí") zastavit vykonávání programu po každé vykonané instrukci / řádku kódu a zůstat zastavené tak dlouho, jak potřebujeme, abychom měli možnost prozkoumat hodnoty ve všech proměnných (tedy míst, v nichž si pro nás program pamatuje určité hodnoty) a abychom mohli ověřit že logika* programu funguje tak, jak bylo zamýšleno.

* Logic: Logic in a computer program is nothing else than deciding on what to do next and how to do it based on what the program knows = what it had stored in those already mentioned "variables".

We all do logic every day: "If it is currently raining, take an umbrella." is a simple logic. A little bit more complicated logic is: "If it is currently raining OR if it is not raining now but it rained the whole day so far and the sky is still dark everywhere we can see, also take the umbrella with you." That is logical for us and we humans do things like this automatically without even having to think much.

On the other hand, a computer is totally dumb and only does EXACTLY what we tell it. So if we tell it "Take the umbrella IF it rains AND it rained the whole day AND the sky is still dark." we have just made a logical error. That happens often when you try to describe things perfectly.

If I said the above to another human, the logical error I just made would likely not even be noticed and would instead be automatically corrected for by the other human I told this to. Remember I said the computer was dumb though? That means that it won't automatically correct my logical error. It will just do EXACTLY what I told it to do, which will in this case result in it NOT taking the umbrella if it just started raining for the first time today, because I told it to take the umbrella if it is raining AND (not "OR") it was already raining before. It was a logical error I made in describing for the computer what I wanted it to do under certain combination of conditions.

By stepping through every line of that code I will probably discover my error since I will be expecting it to take one decision and it will take another instead. That will push me to think why it did so and I will eventually figure out that I must change the AND for OR and that I also have to put the other two conditions after the OR in braces so that it is evaluated as a single decision unit, because otherwise the next time I am stepping through this code, I MAY discover it STILL behaves incorrectly.

The "MAY" above is also important here while I'm trying to explain why programming takes so long. We have to step through the same code again and again multiple times using different input values to really be REASONABLY sure that we tested all the permutations of all the significant values of all the variables involved in such a decision and that ALL of them result in the intended resulting decisions. That is because, depending on the values of conditions we are testing, often even bad logic results in the correct decisions the program makes. For example: The version of my example logic containing the logical error above would still make the correct decision if the values of the variables we are testing would be: IsItRaining = true and HasItBeenRaining=true and IsTheSkyDark=true. It would all seem fine. If I abandoned the testing there I would end up with some very unhappy users.

And even after getting the above bit of logic right, we still may be in trouble. What if it is raining and there is no umbrella available!? Well, a real person would simply have another decision to make: "Am I willing to get wet to get to where I need to go or will I wait for the rain to stop?" However, if I, the programmer, don't think about such a possibility ahead of time and don't explicitly tell the program what exactly to do in such a case, the program will crash!

Maybe there seems to be no possibility for there to be no umbrella available. Maybe the computer version of the person always lives alone so there is nobody else who could have left the house before him and take the one available umbrella with him. So I, the programmer, won't account for such a possibility. Fine. But I may have not realized that under specific set of conditions somewhere completely else in the code, maybe even code written by somebody else, the computer version of the person can actually forget and leave the umbrella somewhere else, returning home without it. So next time it "rains" in the program and the program person needs to leave the house, the program will still crash.

So, well-written program has to be able to deal even with such unanticipated conditions as well as conditions that are completely out of control of that particular program. All that is part of what makes programming not trivial and thus also not fast to do.

We are teaching a completely dumb baby to walk again and again and again, if we are not clever about it. So what I've done by writing so much code above is that I taught the baby to walk and next time I need it to walk I just tell it how fast I want it to walk, not how to move its legs and maintain its balance.

The difference in teaching a real kid to walk and a computer is that I can show the kid and make it repeat and improve. On the other hand, to the computer I have to describe in excruciating detail which muscle to move at exactly what moment and by exactly how much in unison with other muscles which movement I also had to describe the same detailed way. The computer figures out nothing at all for me by itself, unless somebody told it exactly how to do the thing I want it to do.

* Logika: Logika v počítačovém programu není nic jiného, než rozhodování o tom, co udělat jako další krok v závislosti na tom, co program ví, tedy co je uloženo v těch již zmíněných "proměnných" a v jaké fázi se program nachází.

My všichni děláme logická rozhodnutí každý den: "Pokud právě prší, vezmu si deštník." je jednoduchá logika. Trošku komplikovanější logika je: "Pokud právě prší NEBO pokud neprší právě teď, ale pršelo celý den A obloha je stále zatažená kam dohlédnu, také si pro jistotu vezmu deštník." To je pro nás lidi logické a takováto rozhodnutí děláme naprosto automaticky i bez nutnosti nějak obzvlášť vědomě o nich moc přemýšlet.

Na druhé straně počítač je naprosto hloupý a udělá pouze PŘESNĚ to, co mu řekneme. Takže když mu řeknu "Vezmi si deštník KDYŽ prší A KDYŽ pršelo celý den A obloha je stále zatažená.", tak jsem právě udělal logickou chybu. To se stává často, když musíš popsat věci naprosto přesně.

Pokud bych řekl tu větu v předchozím odstavci lidské bytosti, více než pravděpodobně by to, že jsem udělal logickou chybu, nebylo daným člověkem vůbec ani zaznamenáno a místo toho by tato chyba byla automaticky jeho mozkem zkorigována, aniž by si to ten člověk vůbec uvědomil a nějak nad tím přemýšlel. Pamatujete, že jsem však říkal, že počítač je naprosto hloupý? To znamená, že takovou chybu automaticky neopraví, nezkoriguje. Počítač jen udělá PŘESNĚ to, co jsem mu řekl, ať udělá, což v tomto případě vyústí v to, že si NEVEZME ten deštník pokud právě začalo pršet dnes poprvé, protože jsem mu řekl, ať si vezme deštník pokud prší "A" (ne "NEBO", které jsem měl použít) už pršelo celý den předtím. Byla to logická chyba, kterou jsem udělal v popisu pro počítač v tom, jak chci, aby se za určité kombinace podmínek zachoval.

Tím, že se prokrokuji všemi řádky relevantního kódu pravděpodobně objevím svou chybu, protože jsem očekával, že se program rozhodne tak a on se rozhodl onak. Toto rozhodnutí bude prováděno na konkrétním řádku a protože bude jiné, než očekávám, budu ten řádek zkoumat a přemýšlet proč se tak stalo. A eventuálně mi dojde, že musím změnit "A" na "NEBO" ale také možná to, že musím uzavřít ty další dvě podmínky po tom "NEBO" mezi závorky, aby byly posouzeny jako ucelená jednotka. Pokud to neudělám, pak až budu znovu krokovat skrze stejný kód, MOHU objevit že se STÁLE JEŠTĚ nechová správně.

To "MOHU" v předchozím odstavci je zde také důležité v rámci mého pokusu vysvětlit, proč programování trvá tak dlouho. Musíme krokovat skrze stejný kód znovu a znovu mnohokrát s použitím různých vstupních hodnot programu, abychom si mohli být JAKŽ TAKŽ jisti, že jsme otestovali všechny permutace všech důležitých proměnných, na kterých jsou daná rozhodnutí programu závislá a že VŠECHNY tyto permutace vyúsťují vždy v očekávané správné rozhodnutí programu. To proto, že v závislosti na hodnotách proměnných, které testujeme, často i špatná logika vyústí ve správné rozhodnutí programu. Pak se nám může zdát, že již vše funguje správně, zatímco tomu tak ve skutečnosti není. Jen jsme neotestovali tu "správnou" kombinaci vstupních hodnot, abychom naši logickou chybu objevili. Například: Ta verze mého příkladu obsahující logickou chybu výše by stále vyústila ve správné rozhodnutí, pokud by vstupní hodnoty proměnných při testování byly: Prší = ANO a PršeloDoposud = ANO a JeStáleZataženo = ANO. Vše by se zdálo v pořádku. Kdybych testování opustil v tuto chvíli, skončil bych s pár velmi nespokojenými uživateli, protože při jiné kombinaci těchto proměnných by program udělal roznodnutí špatné.

A dokonce i poté, co bychom tu logiku výše konečně správně doladili, mohli bysme být i nadále v problémech. Co když prší a my nemáme k dispozici žádný deštník!? No normální člověk by jen jednoduše stál před dalším rozhodnutím: "Jsem ochoten zmoknout abych se dostal tam, kam se potřebuji dostat, nebo počkám, až přestane pršet?" Avšak pokud já, programátor takového programu, si neuvědomím takovou možnost dopředu a neřeknu mému programu EXPLICITNĚ, co má v takovém případě udělat, program zkolabuje, umře, zdechne! A to se uživateli též bude líbit pramálo.

Možná se zdá býti nemožné, aby program neměl k dispozici deštník. Možná ta počítačová verze člověka vždy žije sama a tak neexistuje nikdo jiný, kdo mohl byt opustit před ním a odnést s sebou ten jediný deštník, který tam mají. Takže já, programátor, s takovou možností ani nebudu počítat. Dobře. Nemusel jsem si ale uvědomit, že za specifických podmínek někde na jiném místě v kódu, možná dokonce kódu, který napsal úplně jiný než já, ta počítačová verze člověka může svůj deštník někde zapomenout, vrátíc se domů bez něho. Takže příště, když bude v programu pršet a ten počítačový člověk bude potřebovat opustit byt, progam stejně zkolabuje.

Takže.. Dobře napsaný program se musí umět vypořádat i s neočekávanými podmínkami, včetně takových, které jsou kompletně mimo kontrolu toho konkrétního programu. Všechno toto je součástí toho, co dělá programování netriviální záležitostí a proto také ne záležitostí pár minut, hodin, nebo dní.

Učíme tu kompletně hloupoučké dítě chodit znovu a znovu, pokud to neuděláme chytřeji. Takže co jsem udělal tím, že jsem výše napsal tolik kódu je, že jsem naučil to dítě chodit a příště, až budu potřebovat, aby chodilo, jen mu už řeknu jak rychle a kam má jít a nebudu muset znovu popisovat, jak přesně pohybovat nohama a jak při tom má udržovat rovnováhu.

Velký rozdíl mezi učením opravdového dítěte chodit a učením toho samého počítači je v tom, že opravdovému dítěti mohu prostě ukázat, jak se to dělá a nechat ho to zkoušet a zlepšovat se v tom. Na druhé straně počítači musím popsat do největších podrobností, který sval zapojit přesně v kterém momentu a přesně jakou silou a to v souhře s ostatními svaly, jejichž pohyb musím též pospat do stejných maxímálních podrobností. Počítač se nebude sám od sebe učit a zlepšovat, nikdy mu nedojdou věci prostě přirozeně, pokud mu neřeknu naprosto PŘESNĚ, jak má dělat tu věc, kterou po něm chci.

The component documentation

Dokumentace k té komponentě

Additionally to the code above, when we program piece of code that is supposed to be reusable it should have some documentation explaining its capabilities and how it's features can be used. I don't necessarily do it for everything I ever write when writing it for my own use only, but it can help immensely even in such cases. In this particular case I did write a help file to remind myself in the future how to use the component. The fact that I also wrote the documentation below, which makes the component so complete and such a stark example between its simplest and advanced versions, made me decide to write this article. So here is also the documentation for the component code above.

Navíc ke kódu výše, když naprogramujeme kus kódu, který má být opakovaně použitelný, by měla být dodána dokumentace, vysvětlující jeho schopnosti a jakým způsobem lze využít jeho funkcí. Toto nezbytně nedělám pro všechno co kdy naprogramuji, když to programuji jen pro vlastní potřebu, ale i v takových případech může v budoucnu dokumentace značně pomoci. V tomto konkrétním případě jsem napsal dokumentaci abych sám sobě v budoucnosti připoměl, jak se dá má komponenta použít. Ta skutečnost, že jsem tu dokumentaci níže napsal, což dělá tuto komponentu dosti kompletní a tak jasně ukazuje rozdíl mezi její nejjednodušší a pokročilou verzí bylo katalizátorem k mému rozhodnutí napsat tento celý článek. Takže zde je také celá dokumentace k používání předcházejícího kódu.

Again, the below is a section you can only scroll through without reading. I included it because it shows there is also this - often non-insignificant - part of programming that also does not write itself. In fact to write this concrete piece of documentation took me probably close to 8 hours of work by itself.

Znovu zde zdůrazním, že touto sekcí můžete jen "proskrolovat" bez čtení. Navíc jsem jí nepřeložil do češtiny, takže zůstává v angličtině. Dávám jí sem protože názorně ukazuje, že existuje také tato, často ne malá, součást programování, která se také nenapíše sama. Popravdě mi jen tento konkrétní kus dokumentace zabral pravděpodobně blízko k osmi hodinám práce.

Not only this part does not write itself, it also does not teach itself. Remember when I said that the computer cannot do the simplest thing unless somebody describes to it exactly how to do it? So if somebody already told the computer what exactly it means "to walk", I do not need to do that again if the other programmer did his job well and designed the "walking program" to be configurable enough so that I can tell it how long and how strong legs I need and so on. (You definitely do not want to give baby legs to a virtual grown person or give the same legs to both a human and a kangaroo, do you?) It needs to be configurable. So I, the programmer who wants to reuse a piece of code like that, which is made into a component, still need to learn how the other programmer made it possible for me to set it up, what walking or running modes I can use and so on. For that, we need a document to learn those things from. The below is an example of just such a document describing the very component I am using as an example in this article.

Nejen že se tato část same nenapíše, ale také se sama nenaučí. Pamatujete, když jsem řekl, že počítač neumí udělat ani ty nejjednodušší věci sám od sebe, dokud pro něj někdo naprosto přesně nepopíše, jak to udělat? Takže když někdo tomu počítači už přesně vysvětlil, co to znamená "chodit", nemusím to já dělat znovu, pokud ten, kdo ho to naučil, udělal svou práci dobře a udělal ten program dostatečně konfigurovatelným tak, abych já tomu programu mohl už jen říci jak dlouhé a jak silné nohy ve svém vlastním programu potřebuji atd. (Určitě nechcete dát nohy novorozence virtuálnímu dospělému člověku, nebo dát stejné nohy jak člověku, tak klokanovi, že ne?) Tyto věci potřebují být konfigurovatelné. Takže já, progamátor, který chce využít takového kusu kódu, který je udělán jako samostatná komponenta, se stále musím naučit, jakým způsobem mi ten druhý programátor umožňuje tu komponentu nastavit, jaké způsoby chůze nebo běhu mohu využít atp. No a na to potřebujeme dokument, ze kterého se takové věci můžeme naučit. To co následuje je právě příklad takového dokumentu, který popisuje přímo tu komponentu, kterou jsem tu já naprogramoval a kterou používám jako příklad v tomto článku.

Before we get to the sample document though, I should point out the other side of the coin here. The fact that somebody wrote a "walking / legs" component does not mean that that is now done and nobody ever needs to write a "walking" component again. Even if the creator of the original component gave it lots of options (which does not happen that often for non-commercial components), then there is still a good chance I will need something it does not offer me. Sometimes there is the ability to extend the already existing component, other times I just have to write my own from scratch anyway. Or, even if it gives me all I need, it may be slow for where and how I need to use it. Or I need wooden legs for a table, not living and moving legs. In such cases I need to write my own "legs" component too. Or I actually need to use only a tiny subset of the component's functionality and the amount of memory it takes is too big for how little I need to use from it. Again, write my own. And for commercial components there is cost on top of these considerations. Many of the really well designed components are expensive. And no wonder as it takes such a huge effort to design and program them as we are presently learning here.

Než se ale dostaneme k příkladu tohoto dokumentu, měl bych tu zdůraznit tu druhou stránku mince. Ta skutečnost, že někdo napsal "chodící / nohy" komponentu neznamená, že to je tedy hotové a nikdo už nikdy nebude muset napsat "chodící" kód znovu.I přesto, že původní tvůrce takové komponenty jí mohl dát mnoho různých možností nastavení (což se nestává často pro nekomerční komponenty), pak stále ještě existuje dost dobrá možnost, že já budu potřebovat nějaké chování, které mi ta komponenta od druhého programátora neposkytuje. Někdy zde pak bývá možnost rozšířit vlastnosti takové komponenty o nové, které jsou potřeba a jindy člověk musí napsat celý kód pro dané chování z gruntu znovu. Nebo i když dostupná komponenta bude dělat vše, co potřebuji, může být zase ale třeba příliš pomalá pro to, kde ji potřebuji potřebuji použít. Kód lze psát mnoha způsoby a každý mohl favorizovat jiné aspekty a třeba zrovna né rychlost vykonávání. Nebo potřebuji dřevěné nohy pro stůl, ne živé a pohybující se nohy. V takových případech musím napsat vlastní "nohy" komponentu také. Nebo ve skutečnosti potřebuji využít pouze malinkatou skupinu všech funkcí, které mi komponenta od někoho jiného poskytuje a zabírá v paměti příliš mnoho místa na to, jak málo z toho, co umí, potřebuji použít. Znovu, napíšu si tedy raději vlastní. A pro komerční komponenty navíc existuje i stránka nákladnosti. Mnohé z těch opravdu dobře nadesignovaných komponent jsou drahé. A není divu, když jejich napsání vyžaduje tak velké úsilí, jak se tu spolu právě učíme.

Anyway, here is my component's documentation:

Tak tady je konečně ta dokumentace:

Example: The component's documentation

Příklad: Dokumentace k prezentované komponentě

And finally I offer you a demonstration of this advanced component's capabilities:

A nakonec vám nabízím demonstraci schopností této pokročilé komponenty:

Demos

Demonstrace

The following are 4 single-line text boxes and 2 multi-line text boxes that all have the character counter component applied to them and each have different settings for the component to demonstrate its versatility.

Následují 4 jednořádková textová pole a 2 mnohořádková, která mají všechna aplikovánu mojí komponentu počítadla znaků. Každé z těchto textových polí má ale jiná nastavení, aby demonstrovala mnohoúčelnost této komponenty.

You can run a simulation in all the boxes at the same time Simulaci můžete spustit pro všechna pole najednou:

Example 1: The textbox has only the maxlength standard HTML property specified:

Příklad 1: Toto textové pole má nadefinovánu pouze standardní HTML vlastnost zvanou maxlength:

<input type="text" class="counter" maxlength="30" />

It will show the intermediate stages based on the component's default settings which are:

  • Do not show the first stage at all.
  • Show the second (highlighted) stage at 80% full = character 24 here.
  • Show third (warning) stage at 90% full = character 27 here.
  • Show the MAX stage and stop accepting any more characters at 100% full.

Zobrazí všechny mezi stupně v závislosti na výchozích nastaveních, která jsou:

  • Nezobrazovat první stupeň vůbec.
  • Zobrazit druhý stupeň ("Pozor"), když je pole na 80% plné = zde znak č.24.
  • Zobrazit třetí stupeň ("Varování"), když je pole na 90% plné = zde znak č.27.
  • Zobrazit čtvrtý stupeň ("Maximum") a zastavení akceptace dalších znaků když je pole plné na 100%, tedy zde znak č.30.

Example 2: The textbox has the data-counter-def instead of the maxlength property specified and its value specifies several things at once.

Příklad 2: Toto textové pole má definovánu vlastnost data-counter-def namísto původní maxlength a její hodnota specifikuje několik věcí najednou.

<input type="text" class="counter" data-counter-def="0,-10,-3,-35,counter-blue" />

That specifies the following counter definition settings To definuje následující nastavení počítadla:
0,-10,-3,-35,counter-blue

It sets the counter to:

  • Show immediately even if the text box is completely empty (0).
  • Use a different look for the first stage of the counter. That is specified by a name of something called a "CSS class" which is here named counter-blue in this example. It will make the counter background light blue.
  • Show its highlight stage when there are only 10 more characters available left to type (-10) = character 25 here.
  • Show its warning stage when there are only 3 more characters left to type (-3) = character 32 here.
  • Show the alert stage when the limit of 35 characters is exceeded (instead of merely reached) (-35) = character 36 here.
  • Let the user continue typing even after the maximum is exceeded because this forth number is negative (-35). (Usefulness of that ability is explained after the last example.)

Takto nastavené počítadlo:

  • Je zobrazeno ihned, pokud má toto pole fokus, i když v poli není ani jeden znak (0).
  • Použije jiný vzhled první fáze počítadla. To je specifikováno jménem něčeho, co se nazývá "CSS třída", která se v našem příkladu jmenuje counter-blue. Toto způsobí, že pozadí počítatla v této první fázi bude světle modré.
  • Zobrazí druhý stupeň ("Pozor") až když bude zbývat pouze 10 znaků do konce (-10) = zde znak č.25.
  • Zobrazí třetí stupeň ("Varování") až když budou zbývat pouze 3 znaky do konce (-3) = zde znak č.32.
  • Zobrazí čtvrtý stupeň ("Maximum") až když je daný limit 35-ti znaků překročen (místo toho, aby byl jen dosažen) (-35) = zde znak č.36.
  • Nechá uživatele pokračovat ve psaní i poté, co je nastavené maximum znaků dosaženo, protože tento čtvrtý parametr je negativní (-35).
    (Využitelnost takovéto schopnosti je vysvětlena po posledním příkladu.)

Example 3: Showcases an alternative vertical, instead of horizontal layout of the counter component.

Příklad 3: Demonstruje alternativní vertikální, místo horizontálního uspořádání počítadla.

<input type="text" class="counter counter-vertical" data-counter-def="1,50%,80%,40" />

That is similar to Example 2, except the settings are as follows:

To je podobné příkladu 2, kromě toho, že definice nastavení vypadá takto:

1,50%,80%,40

It has an additional class named counter-vertical.

Také má nadefinovánu přídavnou "třídu" counter-vertical.

That sets the counter to:

  • Show when the text box contains at least one character and hides it if empty (1).
  • Show its highlight stage at 50% full (50%) = character 20 here.
  • Show its warning stage at 80% full (80%) = character 32 here.
  • Show the alert stage when the limit is reached (40) = character 40 here.
  • Stops it accepting any further input from the user when the count reaches 40.
  • Finally, all the stages of the counter are in a vertical layout.

Toto nastavuje pro počítadlo následující chování:

  • Zobrazit počítadlo když pole obsahuje alespoň jeden znak a skrýt ho, když je prázdné (1).
  • Zobrazit druhý stupeň, když je pole 50% plné (50%) = zde znak č.20.
  • Zobrazit třetí stupeň ("Varování"), když je pole 80% plné (80%) = zde znak č.32.
  • Zobrazit čtvrtý stupeň ("Maximum"), když se v poli nachází 40 znaků.
  • Přestane přijímat vstup (další znaky) od uživatele, pokud se v poli nachází 40 znaků.
  • A všechny stupně počítadla jsou zobrazeny ve vertikálním uspořádání.

Example 4: Showcases collaboration with surrounding code.

Příklad 4: Demonstruje spolupráci s okolním kódem.

<input type="text" class="counter " data-counter-def="1,25,x,50" />

You may have thought that putting a counter into a single-line text boxes is an overkill and very unnecessary since the amount of text expected in them is so small. And you would be right in almost all cases. However, there is always something we never thought of before that creates an exception. And a need to nicely handle just such an exception is another reason that made me actually write this whole component.

Mohli jste si myslet, že ukazovat počítadlo v jednořádkových textových polích je přehnané a naprosto zbytečné, protože délka textu očekávaná běžně v takových textových polích je malá. A měli byste pravdu ve většině případů. Avšak je zde vždy něco, co nás dříve nenapadlo, co vysvětluje výjimku. A právě potřeba se s takovou výjimkou vypořádat je přesně tím důvodem, který mě vedl k napsání této celé komponenty.

In the application I am currently developing called TipOff.Stream, I have a couple of single-line text boxes where the user can specify names for some items. These names can be up to 50 characters long, but when they are longer than 25 characters, they will get truncated when displayed in a quite narrow list of those items. Therefore I want to let the user know about this to give them a chance to come up with a shorter descriptive name which will then help them to more easily find it in the list later. So I need the textbox to do 4 things:

  1. Show the counter after the first character is typed in.
  2. When the content length reaches 25 characters I want to alert the user by changing the appearance of the counter to draw attention.
  3. At the same time I would like to show the user a message explaining why we are drawing his or her attention. This is achieved thanks to the ability of the counter component to announce to other code that its state has changed. This outside code can then also selectively react to the changes that it is interested in. In this case to display the message under the textbox when the "counter highlight" stage is reached (and again hide it if the user makes the name shorter than 25 characters).
  4. And finally, while we are already showing the counter, show clearly that the maximum has been reached.

V aplikaci, kterou právě programuji zvané TipOff.Stream mám (zatím) dvě jednořádková textová pole, která takovou vlastnost potřebují. Tato dvě pole umožňují uživateli pojmenovat určité položky. Tato jména mohou být až 50 znaků dlouhá, ale když jsou delší než 25 znaků, nevejdou se již celá zobrazit v jednom ze seznamů jinde v programu, který je celkem úzký. Proto chci dát uživatelům o tomto vědět a dát jim šanci vymyslet popisné jméno, které se vejde do délky 25 znaků, což jim pomůže jednodušeji tuto položku najít v tomto úzkém seznamu. Takže potřebuji, aby toto textové pole umělo 4 věci:

  1. Zobrazilo počítadlo jakmile je napsán první znak.
  2. Když délka zadávaného názvu dosáhne 25ti znaků, změnit vyzáž počítatla k upoutání uživatelovi pozornosti.
  3. V tu samou chvílí ukázat uživateli zprávu vysvětlující, proč upoutáváme jeho pozornost. Toho je docíleno díky schopnosti této počítadlové komponenty oznamovat ostatnímu kódu, že se její stav změnil. Tento okolní kód pak může, také volitelně, reagovat na změny, které ho zajímají. V tomto případě zobrazit tu zprávu pod tím textovým polem, když je dosaženo druhého ("Pozor") stupně a také tuto zprávu znovu skrýt, pokud uživatel zkrátí zadávaný název pod 25 znaků délky.
  4. A jako poslení ukázat jasně, pokud uživatelův vstup dosáhne maximální délky 50ti znaků.

To achieve that, we need to use these settings for the data-counter-def property of the textbox:

Abychom všechno toto dokázali, potřebujeme použít následující nastavení vlastnosti data-counter-def našeho textového pole:

1,25,x,50

This will:

Toto:

  • Show the basic, inconspicuous version (first, basic stage) of the counter as soon as the user enters at least 1 character.
  • Show the highlighted stage of the counter at 25 characters.
  • Shown no warning stage.
  • And if the maximum of 50 is reached, it will change from highlighted directly to the alert stage of the counter and will stop accepting any further user input.
  • Zobrazí základní nenápadnou verzi našeho počítatl jakmile uživatel napíše první znak.
  • Zobrazí zvýrazněnou verzi počítadla jakmile tato komponenta napočítá 25 znaků v tomto poli.
  • Nezobrazí žádný varovací mezistupeň.
  • A pokud délka zadaného textu dosáhne 50ti znaků, přejde rovnou ze zvýrazněné verze na verzi "Maximum" a přestane akceptovat jakékoliv další znaky.

To test the message showing type at least 25 characters in the following box.

K otestování tohoto chování napiš alespoň 25 znaků do následujícího pole.

Example 5: Standard multi-line text box behaviour of the component.

Příklad 5: Standardní chování této komponenty v mnohořádkovém textovém poli.

<textarea  class="counter" maxlength="120">

Multi-line text boxes cannot really show the counter inside them as that would be either ugly or even uglier. So the component shows the counter below them and once shown, it stays there as opposed to it disappearing like it does by default from the single-line textboxes when they loose focus.

Mnohořádková textová pole nemohou dost dobře zobrazit počítadlo uvnitř nich samých, protože to by vypadalo nehezky. Proto tato komponenta zobrazuje počitadlo znaků pro tento typ textových polí pod nimi a jak je jednou počítadlo zobrazeno, už nikdy nemizí ani po strátě fokusu, na rozdíl opačného chování v jednořádkových textových polích když přestanou být aktivní.

The maximum length here is set to 120 characters. The data-counter-def property is not used here so default values for the counter thresholds will be used:

  • The counter will first appear at 50% = 60 characters.
  • It will be highlighted at 80% = 96 characters.
  • It will change its colour to warning colour (orange) at 90% = 108 characters.
  • And it will prevent any further user input at 120 characters and show the counter with the alert (red) background.

Maxímální délka je zde nastavena na 120 znaků. Ten atribut data-counter-def zde není použit, takže platí základní nastavení této komponenty:

  • Počítadlo se poprvé objeví dosáhne-li počet znaků v textovém poli 50% = 60 znaků.
  • Počítadlo bude zvýrazněno při dosažení 80% = 96 znaků.
  • Pak změní znovu svou barvu (tentokrát na oranžovou) při dosažení 90% = 108 znaků.
  • A zabrání uživateli vkládat další znaky při dosažení 100%, tedy 120ti znaků, zatímco změní automaticky svou barvu na červenou.

Example 6: More descriptive, soft-limited, auto-hiding counter.

Příklad 6: Více popisné, "měkce" omezené, samoskrývající se počítadlo.

<textarea  class="counter counter-hide-on-blur" data-counter-def="1,20,60%,-120">

A soft limit means that the text box actually lets the user to write more than the maximum allowed number of characters (same as in Example 2), reason for which is explained below the following test text box. The settings are:

"Měký" limit znamená, že dané textové pole ve skutečnosti nechá uživatele pokračovat ve psaní i poté, co počet zadaných znaků dosáhl maxima (stejně jako v Příklad 2). Důvod pro požadavek takového chování je vysvětlen pod následujícím testovacím polem. Nastavení jsou následující:

1, 20, 60% -120

That means that this text box:

To znamená, že toto textové pole:

  • Will show the counter when it contains at least one character (1).
  • Will show the highlight state at 20 characters (20).
  • Will show the warn stage at 60% = character 72 here.
  • Will show the alert stage at character 120, but WILL LET the user continue typing.
  • Zobrazí počítadlo, když obsahuje alespoň jeden znak (1).
  • Zobrazí zvýrazněnou verzi počítadla při 20ti znacích v poli (20).
  • Zobrazí varovnou verzi počítadla, když textové pole dosáhne 60% plnosti = zde znak č.72.
  • Zobrazí poslední ("Maximum"), červenou fázi při dosažení 120ti znaků, ale NECHÁ uživatele pokračovat v psaní.

Why let the user type past the maximum limit?

Proč nechat uživatele psát i po dosažení maximálního limitu?

This functionality is intended for multi-line text boxes with fairly large maximum limits set.

Tato funkčnost je určena pro mnohořádková textová pole s poměrně vysokými maximálními limity.

Imagine that you, as a user of an application are typing your feedback into such a textbox and even though you can see the counter, you reach the maximum limit before you really finished your thought. If the program stopped you right there, you would have to first review what you wrote to try to shorten or leave out some of your previously written sentences. Only then you would be able to come back to the end and enter the rest of your last sentence if you managed to remove enough characters. By now though, you may not remember exactly what you actually wanted to say in that last sentence of yours. For this reason it may be much more user friendly to let the user finish their thought first and only then try to shorten the whole text to fit it back within the maximum limit.

Představ si, že ty jakožto uživatel aplikace píšeš tvou zpětnou vazbu to takového textového pole. Přestože vidíš počítadlo znaků, dosáhneš maximální povolené délky ještě před tím, než dokončíš svou myšlenku. Kdyby tě program zastavil přesně na maximu, musel(a) bys první revidovat svůj dosud napsaný text za účelem jeho zkrácení a až pak by ti bylo povoleno dokončit tvou větu, tvou myšlenku, pokud se ti podařilo předchozí text dostatečně hned na poprvé zkrátit. Tou dobou už si mohl(a) zapomenout, co jsi chtěl(a) napsat. Proto může být uživatelsky mnohem příjemnější, když tě program nechá tvou myšlenku dopsat a jen ti následně pomůže svým interaktivním počítadlem znaků zkrátit celkovou délku tvého textu dostatečně k tomu, aby ho bylo možné odeslat.

We only demonstrate it during the simulation on a single-line text (Example 2) box because it is easier to show. However, you can try this manually in the multi-line textbox just above. Just enter more than 120 characters to see the alert version of the counter show but you will be able to continue typing.

V simulaci je toto demonstrováno pouze v jednořádkovém textovém poli (Příklad 2), protože je to jednoduší tak demonstrovat. Avšak můžeš si to vyzkoušet manuálně v druhém mnohařádkovém poli výše. Pouze vlož více jak 120 znaků ke zobrazení "Maximum" fáze počítadla, ale budeš moci přesto pokračovat dál ve psaní. Po zkrácení na přípustnou úroveň ti dá počítadlo vědět.

Once you've ran the simulation seeing all the text boxes on one screen, you can click the check box below to show the explanations of the differences how the component was set up and why it behaves the way it does for each of the text boxes.

Jakmile jsi si vyzkoušeli simulaci, kdy byly všechna textová pole viditelná najednou, můžete kliknout na zaškrtávací pole níže aby se ukázala vysvětlení všech rozdílných chování v závislosti na tom, jak bylo které nastaveno. Stránka se posune znovu na začátek příkladů.


By the way...

Mimochodem...

Just to demonstrate how "buggy" new code almost always is, even if already tested, I'll show you here just one of the bugs I discovered and had to fix, while creating the examples above.

Jen na ukázku, jak má nový, i již otestovaný kód stále ještě mouchy, ukáži vám zde jen jednu "mouchu", kterou jsem objevil a musel opravit, když jsem vytvářel ty příklady výše.

I already told you that to be sure that our code is actually really doing exactly what it was designed to do, we must test / debug extensively. And because the showcased component is not meant - at least at this stage - to be offered publicly to other programmers to use and I'm already behind my project schedule a huge bit, I have actually NOT tested the component enough for all the conditions I designed it for. I only tested it for the use cases in which I use it so far in my project and for all the additional conditions and edge cases that I could think of at the time. But even coming up with all the possible combinations of values for tests is not easy and takes time so I was bound to miss a few permutations.

Už jsem vám vysvětlil, že k tomu, abychom si mohli býti dostatečně jistí, že se náš kód skutečně chová tak, jak jsme ho zamýšleli, musíme hodně testovat a "odhmyzovávat". A protože prezentovaná komponenta není míněna jako veřejná, tedy alespoň ne v současné fázi, k použití ostatními programátory a já jsem s programováním mého softwaru, jehož součástí tato komponenta je, hodně pozadu, ve skutečnosti jsem tuto komponentu NEOTESTOVAL na všechny podmínky a mezní případy, ke kterým je určena. Otestoval jsem ji pouze v podmínkách, pod kterými ji potřebuji použít v rámci mého většího projektu a pár dalšími, které mě napadli v ten den. Ale ani vymyslení všech možných kombinací vstupních hodnot není jednoduché a zabírá čas, takže bylo jasné, že zapomenu na pár důležitých permutací

So as I was setting up "Example 2" above, I found out that it actually does not show the counter when you first click it. It did so only after you type the first character in it. That was wrong as it should have shown immediately after gaining focus if the value for the first setting is set to 0 like in that example. It did work correctly after you cleared it again, but not the first time. See, we have ourselves an "edge case" ;-).

Takže když jsem nastavoval "Příklad 2" výše, zjistil jsem že nezobrazuje to počítadlo jakmile se to pole stane aktivním (neboli když "obdrží fokus"). Udělalo to jen po napsání prvního znaku. To bylo špatně. Počítadlo se má ukázat okamžitě po získání fokusu, pokud první část parametru je nastavena na 0, jak to je v tom příkladě. Fungovalo to správně poté, co člověk nějaké znaky do toho pole napsal a zase je vymazal, ale ne po zcela prvním obdržení fokusu. Vydíte, narazili jsme na první neošetřený "mezní případ" ;-).

To fix it, I had to update a couple of functions in the earlier presented complete code, whose changed parts I include below so that you can again imagine even better what programming is about and why it takes time:

K jeho spravení jsem musel upravit pár funkcí v dříve prezentovaném kompletním kódu, jehož pozmněněné části uvádím níže, abyste si opět uměli lépe představit o čem je programování a proč zabírá tolik času:

Function ReShowCounter changed from:

Funkce ReShowCounter změněna z:

function ReShowCounter(el) {
    //...
    if (data) {
        ShowCounter(el);
        //...
    }
}

to:

na:

function ReShowCounter(el) {
    //...
    if (data || (el.data("counter-def") &&
     el.data("counter-def").length &&
     el.data("counter-def").substr(0, 1) == "0")) {
        ShowCounter(el);
        if (!data) {
			data = el.data("counter-data");
			counter = el.next();
        }
        //...
    }
}

This one is an example of a logical error that I described earlier. I just did not think about that particular possible combination of circumstances when first writing and debugging that function.

Tohle je příklad logické chyby, jakou jsem popsal výše. Prostě mě nenapadlo pomyslet na tuto konkrétní kombinaci okolností, když jsem poprvé psal a "odhmyzovával" tuto funkci.

NOTE: The "//..." bits are just placeholders for code that I have removed for clarity to keep only the relevant, changed parts. The complete functions can be found in the complete code presented previously.
Why do those lines start with "//"? That's because two slashes mean "This is just a comment" to the computer and the code visualizer I used on this page. It gives the line a different colour as a visual helper.
POZNÁMKA: Ty řádky níže obsahující "//..." kousky představují kód, který jsem v těchto příkladech odstranil, neb nebyl relevantní. Ponechány jsou jen pozměněné kousky. Kompletní verze těch funkcí jsou k dohledání v kompletním kódu uvedeném dříve.
Proč ty řádky začínají dvemi lomítky? To proto, že "//" v kódu říká počítači že "Toto je jen komentář." a mimo jiné díky tomu dostanou i jinou barvu, což slouží jako vizuální pomůcka.

The second chage was in HideCounter function that changed from:

Druhá změna je ve funkci HideCounter, která se změnila z:

function HideCounter(el, skipEvent) {
    //...
    if (data && data.isShown && (
     (!data.isTextArea && !el.hasClass("counter-keep-on-blur"))) ||
     (data.isTextArea && el.hasClass("counter-hide-on-blur"))) {
        //...
    }
}

to:

na:

function HideCounter(el, skipEvent) {
    //...
    if (data && data.isShown && (
     (!data.isTextArea && !el.hasClass("counter-keep-on-blur")) ||
     (data.isTextArea && el.hasClass("counter-hide-on-blur")))) {
        //...
    }
}

The bug in this second function is an example of another logical error, which, however, is more like a syntax error in this case as it was not my intention to write it that way to start with.

I mean can you even spot the difference between the two versions!?

Ta "moucha" v téhle druhé funkci je příklad další logické chyby, která ovšem v tomto případě vznikla spíše jako syntaktická chyba, neboť nebylo mým záměrem její kód napsat tak, jak byl nakonec napsán.

No vidíte vůbec ten rozdíl mezi těmito dvěmi verzemi!?

Imagine that in real-life you actually don't have this comparison to help you out. You have to figure out what's wrong just by looking at the first bit of the two pieces of code!

Představte si, že v reálném životě nemáte tyto dvě verze k porovnání, aby vám to pomohlo. Máte jen tu první. A na tu chybu musíte přijít bez možnosti porovnávat se správným řešením. Ten druhý, opravený kus kódu neexistoval, dokud jsem to neopravil.

If you found it, good on you. Yes, it is the braces. They are significant there. I had one more closing brace on the line 4 and one less on line 5. It was therefore syntactically correct (since the pairs of opening and closing braces matched in the expression), but it was causing a logical error under some circumstances.

Pokud jste ten rozdíl našli, gratuluju. Ano, jsou to ty závorky. Jsou zde (jako ostatně většinou) významné. Měl jsem jednu navíc na řádku 4 a naopak jedna chyběla na řádku 5. A protože se počet otevíracích a zavíracích závorek shodoval, byl tento kód syntakticky správně, ale za určitých podmínek způsoboval logickou chybu. Ale JEN ZA URČITÝCH PODMÍNEK!!

If you are not interested to know why, skip the next paragraph.

Pokud vás nezajímá proč, můžete následující odstavec přeskočit.

So why then? Well, because it is first testing whether or not the variable called "data" has any value. If it does not, it is supposed to skip all the code inside the curly braces represented here just by the three dots (...) for brevity. If the variable value does exist, it is an object, which has some properties. So we can then test further if the value of the property called "isShown", which we know it contains, is "TRUE". If it is, only then we are interested in testing values of another property of that object called "isTextArea" plus testing whether the function input value called "el" (= element) does or does not have a class called "counter-keep-on-blur" or "counter-hid-on-blur". However, the one wrongly closed brace caused that the code was instead trying to test the value of the property "isTextArea" of the "data" object even though that object does not exist in certain cases. And you do not have to be a programmer to know that you cannot test a property of something that does not exist, do you. And that very thing does not make sense to the computer either, so it "throws an exception", which means that it stops executing the program and notifies us that there is an error in it. This causes software crashes.

Tak proč tedy? No proto, že nejdříve testuje jestli proměnná zvaná "data" má nebo nemá zadanou nějakou hodnotu. Když nemá, ten program má přeskočit všechen kód, který se nachází mezi těmi složenými závorkami, který jsem nahradil pro stručnost jen třemi tečkami (...). Pokud ta proměná má nějakou hodnotu, pak tato hodnota reprezentuje "objekt", což je datová struktura obsahující více informací. Takže pak můžeme testovat dále a zjistit, zda hodnota informace zvané "isShown", kterou v tom případě víme, že ten objekt obsahuje, je "PRAVDA". Pokud je ta hodnota "PRAVDA", pouze tehdy máme zájem otestovat hodnotu další informace, kterou ten objekt obsahuje, zvané "isTextArea" a navíc potřebujeme zjistit, zda vstupní hodnota té funkce zvaná "el" (= element) má nebo nemá třídu zvanou "counter-keep-on-blur", nebo "counter-hid-on-blur". Avšak ta jedna špatně uzavřená závorka způsobila, že se ten program snažil testovat tu vlastnost objektu "data" zvanou "isTextArea", přestože tento objekt za určitých podmínek neexistuje. A k tomu nemusí být člověk programátorem aby mu bylo jasné, že není možné testovat vlastnost něčeho, co neexistuje, že ne. :-) A ta samá věc nedává smysl ani počítači, takže ten takzvaně "vyhodí výjimku", což znamená že přestane vykonávat váš program a informuje vás, že v něm máte chybu, protože jste mu přesně nepopsali, jak se má za takové situace zachovat. Toto způsobuje krachy programů.

And while we are here... An example of a real syntax error would be for example if one of the closing or opening braces was missing so they would not end up making matching pairs. Yes, we often have to count the number of opening and closing braces too, to figure out if and where we are missing a brace. :-) Fortunately, modern programming tools help us match corresponding opening and closing braces.

A když už jsme zde... Příklad opravdové syntaxní chyby by například byl, kdyby jedna z těch uzavíracích nebo otevíracích závorek úplně chyběla tak, že by netvořili kompletní souhlasné páry. Ano, programátor musí často počítat kolik otevíracích a zavíracích závorek v kódu má, aby zjistil, zda mu někde nějaké nechybí :-). I když dnes mu v tomto většinou pomáhají programovací nástroje, které zobrazují, která otevírací závorka patří ke které uzavírací. I tak to lze přehlédnout.

So now you know

Tak a teď víte

So now you can imagine (a little) better what programming is about and that great results cannot be churned out like on a conveyor belt. Every problem is different and the one presented on this page was in fact really quite a simple one. There are hundreds or more likely thousands of problems like this and bigger, much bigger, to solve in a software of any decent size.

Tak a teď si umíte (trochu) lépe představit, o čem programování je a že skvělé výsledky se nedají chrlit jako na běžícím páse. Každý problém je jiný a ten, který jsem prezentoval na této stránce byl ve skutečnosti z těch jednoduchých. V sofwéru jakékoliv solidnější velikosti je k vyřešení takových a mnohem, mnohem složitějších stovky, nebo spíše tisíce.

I'm sure that it is still pretty hard to imagine how code is written, but then just imagine yourself writing it like a book (which I already explained is really NOT like programming at all, but imagine just that).

Jsem si jist že je pro většinu NEprogramátorů pořád docela těžké si představit, jak se píše kód, ale pak si prostě jen představte psát ho jako knihu, což, jak už jsem vysvětlil dříve, tak opravdu NENÍ, ale pro zjednodušení si to tak představte.

And imagine you simply knew what to write in those lines from the top of your head. You would not have to stop and think about it first, you would not have to first imagine the visual layout of things, than maybe design them in a mock-up software and then you would not have to replicate the intended visual look by precisely describing it in code while keeping in mind that many of those visual elements need to be able to resize themselves correctly in harmony with all the other elements on the page etc. You would also not have to first thing about the logic of it, how it should behave by itself, how it should behave in relation to other related data that is being processed and the thinking of the logic would not be stopping you on every n-th line etc. etc.

A představte si, že byste rovnou věděli, co v těch řádcích psát. Nemuseli byste se zastavovat a první o tom přemýšlet, nemuseli byste si první představovat vizuální uspořádání komponent, pak je třeba ještě navrhovat v nějakém návrhovém softwaru a nakonec je replikovat v kódu aby vypadali tak, jak jste je navrhli. Nemuseli byste přemýšlet nad tím, jak se tyto vizuální komponenty musí přizpůsobovat automaticky změnám ve velikosti oken, ve kterých jsou na počítači zobrazeny, nebo různým velikostem displejů, v souhře s ostatními komponenty na dané stránce. Také byste nemuseli první přemýšlet o logice všech těch věcí, jak by se měli chovat vzhledem k ostatním a vzhledem k měnícím se vstupním údajům a přemýšlení o logice celé vaší knihy by vás nezastavovalo na každém n-tém řádku atd.atd.

You would also not have to spend any time debugging what you wrote.

Také byste nemuseli trávit žádný čas "odhmyzováváním" toho, co jste napsali.

You would just write it once and it would just magically work the first time you ran it. Even then you would have to write all that code similar to what was presented earlier times hundreds or thousands or more.

Jen byste to jednou napsali a ono by to zázračně hned fungovalo poprvé, co byste to spustili. I tak byste museli napsat všechen ten kód podobný tomu, co jsem zde prezentoval, krát stovky či tisíce a víc.

Can you imagine? Do you still think that it does not make sense that software development is so slow? And I have not even mentioned everything yet. Read on.

Umíte si to představit? Jste pořád toho názoru, že nedává smysl, že vývoj sofwaru je tak pomalý? A to jsem ještě ani kompletně vše nezmínil. Pokračujte ve čtení.

And there is more

A je toho víc

Even after you've finished coding and debugging your code, you are not done if your code is intended to run in an Internet browser like Firefox, Google Chrome, Edge and others. Nowadays that's most of the software being written. Each one of these browsers is created by a different company and implements certain level of compliance with the continually improving standards for HTML (that is the language that describes the content of any page), CSS (that is the "language" that describes the position and visual appearance of things on the page) and JavaScript (which is the actual programming language thanks to which web pages seem pretty intelligent nowadays). And each of these browsers have many versions with the newer ones being more compliant to the current standards and the older ones, understandably, less compliant.

I poté, co jste zkončili psaní a odhmyzování vašeho kódu, nejste hotovi, pokud váš program má běhat v Internetovém prohlížeči jako je Firefox, Google Chorme, Edge a další. V současnosti to je většina sofwaru, který je vyráběn. Každý jednotlivý z těchto prohlížečů je vytvořen jinou firmou a implementuje různou úroveň dodržovaní dohodnutých a průběžně doplňovaných standardů pro HTML (to je "reč", která popisuje obsah jakékoliv webové stránky), CSS (což je zase "řeč", která popisuje umístění a vizuální prezentaci komponentů na stránce) a JavaScriptu (což je programovací jazyk, díky kterému se jeví současné webové stránky poměrně inteligentní a nejsou jen statické). A každý z těchto prohlížečů má mnoho verzí, kde ty novější dodržují více těchto standardů a starší pochopitelně méně.

The result is that what you just programmed may (and will) look and behave differently in different browsers and different versions of those browsers, all of which being in use by the Internet users of the world. Often, the differences may only be visual and in a way that does not affect usability of the web application. But sometimes even pixel-level (a pixel is one light dot on your screen that, together with millions of its brothers and sister, helps to make up what you see on it) can break the whole layout. It should not, but again, knowing the right way to do things is important here. Often, too, some features are completely broken on some browsers since those may not support an important feature that the developer used in creating his application.

Výsledkem je, že to, co jste právě naprogramovali může (a bude) vypadat a chovat se jinak v rozdílných prohlížečích a v rozdílných verzích těch samých prohlížečů, přičemž všechny ještě stále někde na světě někdo používá. Často jsou ty rozdíly jen vizuální a neovlivňující funkčnost dané aplikace. Ale někdy i rozdíl na úrovni pixelu (pixel je jedna z těch milionů světelných teček, které tvoří obraz na displeji), může rozhodit celé vizuální rozložení aplikace. Nemělo by, ale znovu i zde je důležité vědět, jak dělat věci správně. Často jsou také některé schopnosti kompletně pokažené v určitých prohlížečích, protože ty nemusí všechny podporovat nějakou modernější důležitou funkci, kterou programátor ve svém produktu využil a bez níž jeho aplikace není schopná poskytovat zamýšlené služby.

So now we, additionally, have to test extensively in many different browser / version combinations and try to fix what's broken. That entails programmatically* testing if a particular feature is available in the browser our application is currently running in and if not, either omit the advanced behaviour there, or, if it is vital to the program as a whole, we must come up and code a specific work-around solutions for such cases. These by themselves can be pretty long. As you can probably imagine by now, this can be a very time consuming exercise by its own right as well. We have to deal with visual inconsistencies, behaviour inconsistencies and completely unavailable features in older browsers.

Takže teď navíc musíte ještě testovat velice důkladně vše znovu a znovu v různých prohlížečích a kombinací jejich různých verzí a pokoušet se opravovat nalezené problémy tak, abyste naopak nepokazili něco, co v jiných prohlížečích už dobře funguje. Spravovat takové problémy pak obnáší přidání podmínek do programu, kde musí program testovat, jestli je daná požadovaná funkce vůbec dostupná v prohížeči, kde naše aplikace u uživatele na počítači právě běží a buď v takovém případě skrýt část funkčnosti našeho programu, která využívá té funkce, kterou daný prohlížeč neumí, nenabízí, a nebo musíme vymyslet, jak stejného, nebo alespoň podobného výsledku můžeme v naší aplikaci dosáhnout s tím, co máme ve starším prohlížeči k dispozici. Tyto programové "náhražky" jsou kolikrát mnohem složitější na naprogramování, než naše původní řešení a musí běžet paralelně s ním. A doufám, že si teď umíte představit, že tohle může samo o sobě být hodně časově náročné "cvičení". Musíme se vyrovnávat s vizuálními nesrovnalostmi, s nesrovnalostmi v chování a s kompletně nedostupnými funkcemi starších prohlížečů.

Also, I put an asterisks after the word programmatically. That's because what I'm talking about there is that we must often write in a way of additional code. Completely separate to that, there is "user testing", which involves running the component or the complete program through its paces as its ordinary users will when it is release to them. This different, additional and important kind of testing should be done thoroughly, involving hundreds of different tests (or at least dozens for a simple component) confirming inside a real system that it does what it is designed to do and that it reacts correctly to all the different ways different users can interact with it. User testing itself would warrant its complete separate article on its own and any software company of any decent size has a whole team(s) of people dedicated to just doing that. A lone programmer working on his or her own software has to do that on top of all the already described necessities in this job / science / art.

Také, kromě programatického testování, musíme testovat uživatelsky, tak, jak daný software budou používat obyčejní uživatelé. Tento typ testování je také velice důležitý a měl by být dělán důkladně, což zahrnuje stovky různých testů (nebo minimálně desítky pro jednoduché komponenty), které musí potvrdit uvnitř skutečného systému, že to celé dělá, co to dělat má a že to reaguje správně a předvídatelně na všechny možné způusoby, kterými uživatelé kdy mohou s daným programem interagovat. Uživatelské testování by si zasluhovalo samostatný článek a každá sofwérová firma jekékoli solidnější velikosti má celé týmy lidí určených jen na uživatelské testování. Samotný programátor pracující na jeho vlastním programu to musí dělat navrch všech už zmíněných nezbytností této práce / vědy / umění.

We also have to cater for the many screen sizes that are being used nowadays going from a small mobile phone to large desktop screens. For an application to look right and be user friendly the whole layout as well as some behaviour of the page must change, sometimes drastically. That results in yet more complexity and therefore work for us to create the same thing to behave appropriately on all these screen sizes.

V dnešní době musíme také vytvářet softwér, který vypadá dobře na všech různých velikostech obrazovek a displejů od mobilních po velké desktopové. Aby dnes jakákoliv aplikace vypadala dobře a byla uživatelsky přívětivá, musí umět změnit celé své rozvržení v závislosti na tom, kolik místa na své zobrazení má. Toto rozvržení je nutné v některých případech změnit poměrně drasticky. A to znovu přídává jen další úroveň komplexnosti a tím tedy práce pro nás programátory a softwarové designery.

And we are still not done! Operating an application on a desktop with a mouse actually behaves quite differently when you swap the mouse for your fingers and use a touch screen instead. Suddenly we also have to react differently to figure out if the user wants to scroll, or is pinching to zoom, or is doing a different touch gesture. We receive different events with different values from the system to inform us about what the user is doing in our program and we have to programmatically decide what it means and make it match the same behaviour as if the mouse was used.

A pořád ještě nejsme hotovi! Ovládání operace na desktopu mýší se ve skutečnosti značně liší od chování a ovládání, když je ta myš vyměněna za prsty a dotykový displej. Najdenou musíme reagovat jinak abychom zjistili, jestli uživatel chce posunout obraz, či chce dvěmi prsty zvětšovat či zmenšovat, nebo dělá jiné dotykové gesto. Od systému obdržíme jiné hodnoty a jiné události v závislosti na tom, jak a čím s počítačem uživatel komunikuje. A náš program se musí rozhodnout, co to znamená a musí provést stejné úkony nezávisle na tom, jestli ta komunikace probíhá přes myš a klávesnici, nebo přes dotyk.

So here I should point out that the advanced code for the simple component presented earlier does not even deal with any of these just mentioned issues. That has two reasons:
  1. The component itself is not really interactive,
  2. and for now I only need to use it in an application that I'm building for other developers who I can just inform to use a particular browser (Google Chrome in this case) if they want to use it. Most of us developers have multiple browsers installed on our machines since we need to test in them, so it should not present a big usability problem for version 1.0 of my application. It is not ideal, but it is a very advanced application whose advanced functionality partly relies on the latest HTML and CSS standards. But it is not ideal nevertheless and later versions will be tested and fixed to run in other browsers as well.

    There is just not ever enough time to do everything in version 1 (or 2, or 3 or 4 for that matter) and that si why applications have versions as features and capabilities are added with more development time being able to be invested in them).

    Also, by the time the higher versions are to be released, more and more people will be using more and more of the latest browsers whose development teams will have had by then more time to implement more of the latest standards specifications into their browsers themselves. And that will save lots of my development time on this particular piece of software I'm building.

    There will of course always be people who are using some very old browser versions and such people will unfortunately have more and more problems using the web as we developers have to set somewhere the limit of how far into the past we want to support our products. We cannot write hundreds of workarounds involving thousands lines of code and making it more complicated just to support the let's say 0.5% of users who still live in the year 2005 as far as their browser version is concerned and take that time from working on new, actually exciting features. IT technology is and has been developing very rapidly and we now have hundreds and hundreds of new features that we can use in our development that we could not use back then. And the further back in time it is the more impossible it is to emulate those new features in old browsers. So we simply must stop supporting the old browsers at some point.

Tak a na tomto místě bych měl zdůraznit, že aní můj pokročílý kód pro to jednoduché počítadlo, který jsem zde prezentoval, se vůbec nezabývá těmi probémy, které jsem právě zmínil. To má dva důvody:
  1. Ta komponenta ve skutečnosti nevyžaduje přímo žádnou interaktivitu. Ta je zařízena souvisejícím textovým polem.
  2. A pro teď já ji pouze potřebuji použít v aplikaci, kterou programuji pro ostatní programátory, které prostě mohu jen informovat aby používali konkrétní Internetový prohlížeč, pokud jí chtějí používat. Většina programátorů má nainstalovány všemožné prohlížeče, protože jak už jsem napsal, musíme v nich naše produkty testovat. Takže takový požadavek by neměl představovat žádný problém v použitelnosti verze 1.0 mé aplikace. Není to ideální, ale má aplikace je velmi pokročilá a její funkčnost částečně záleží na nejposlednějších standartech HTML a CSS. Nicméně není to ideální a další verze mé aplikace budou testovány a poběží i na jiných prohlížečích.

    Prostě člověk nemá nikdy tolik času, aby udělal vše už ve verzi 1 (nebo 2, nebo 3, když už jsme v tom). A proto mají aplikace více verzí tak, jak jsou jednotlivé funkce a schopnosti prostupně přidávány s narůstající dobou, jakou jim můžeme věnovat.

    Jistě budou vždy existovat lidé, kteří používají velmi staré prohlížeče a tito lidé budou mít bohužel více a více problémů v používání Internetu, neboť my vývojáři sofwéru musíme někde stanovit limit, jak daleko do minulosti jsme ochotni podporovat naše produkty. Není v našich silách psát stovky a stovky oklik obsahující tísíce řádků kódu, které náš softwér dělají mnohem komplikovanější, než už tak je jen proto, abychom umožnili řekněme půl procentu uživatelů, kteří žijí stále ještě v roce 2005 používat náš softwér. Čas strávený na podpoře starých systémů ubírá z toho, který jinak můžeme věnovat vývoji nových funkcí, které ocení většina uživatelů.

    Počítačové technologie se vyvýjejí velmi vysokým tempem a my máme k dispozici stovky a stovky nových funkcí, které můžeme použít k vytváření lepších a lepších vlastních produktů a které jsme neměli k dispozici dříve. A čím dále do minulosti jsme ochotni zajít, tím více nemožné je emulovat tyto nové funkce ve starých prohlížečích. Takže prostě musíme přestat v určitém bodě podporovat zastaralé verze.

So no, I am not lazy or slow. Programming anything meaningful and doing it well simply takes a bloody long time. And the wider audience of users you are making the software for the more time it takes.

Takže ne, nejsem ani líný, ani pomalý. Naprogramovat cokoliv, co má význam a udělat to dobře prostě zabírá zatraceně hodně času. A čím širší obecenstvo uživatelů, pro které se daná aplikace programuje, tím více času to zabere.

But...

Ale...

...Try to explain this to someone who never programmed anything. Even us programmers, we are pretty bad in estimating how long programming a particular feature of any significant complexity will take us.

...Zkuste to vysvětlit někomu, kdo nikdy nic nenaprogramoval. I my, programátoři, jsme poměrně špatní v odhadování toho, jak dlouho bude něco určité solidní komplexnosti trvat udělat.

Actually, it is not necessarily that we are bad in it, but that it is really almost an impossible thing to pull off right most of the time. The times an estimate actually works are usually either because:

  • We simply were pretty lucky to get this one right, which is really rare.
  • We are one of the faster members of our team so we can afford estimating longer than we think since at the end of the day the estimate is about similar to what some of the slower members would come up with, except its done by the deadline.
  • Or it was a small thing without much likelihood to get more complicated than we originally thought.
  • Or we were pushed by a hard corporate or client-promised deadline and had to compromise on many things, cut down features and generally do things more in a quick-and-dirty way instead of doing them right as we would prefer.

    And this is the one most likely reason enabling us to actually hit the deadline. This is, unfortunately, the mode most corporate programming jobs end up in sooner or later (usually sooner). And it does not make us programmers very happy. Most of us anyway, I'd hope. I, for one, want to be proud of my work, not ashamed for it. It is great to do something quickly - and I've been usually (and also unfortunately) the one who is asked to do it in such cases - but it is even greater to do them also right. And that usually cuts into the "quick" part.

    Sure, the client does not usually know it could have been so much better or that we were forced to take a shortcut that may result in limited future extensibility of the system we built for them. But we know! And it makes it, at least for some of us, so much harder to work for a particular corporation long-term.

    In case of corporations the bosses often actually realise what they are asking us to do but "it is OK, do it quick-and-dirty now and there will be budget in the next budgeting period to do it better all over again". Sometimes that really means re-doing the whole thing almost from scratch. Overall, it does not make sense, but it does in the realm of constraints of corporate budgets. I guess.

Vlastně to možná ani není proto, že jsme v odhadování času špatní, ale prostě je správné odhadnutí času prakticky většinou téměř nemožné. Jediné případy, kdy časový odhad na softwér nakonec jakž takž sedí jsou:

  • Jednoduše jsme měli v tomto případě štěstí, že jsme se trefili, což je opravdu řídký případ.
  • Jsme jedním z rychlejších členů našeho týmu, takže si můžeme dovolit odhadnout delší čas, než si ve skutečnosti myslíme, protože nakonec je ten odhad podobný těm, co by udělali pomalejší členové našeho týmu, jen s tím rozdílem, že to je hotové do stanovené lhůty.
  • Nebo to byla malá věc bez velké šance na to, že se zkomplikuje.
  • Nebo jsme byli tlačeni tvrdou firemní, nebo klientovy slíbenou lhůtou a museli jsme díky tomu nakonec udělat kompromis u mnoha funkcí, vypustit některé funkce kompletně a obecně dělat věci více "rychle a špinavě" místo dělat je dobře, tak jak bychom sami chtěli.

    A toto je ten jenpravděpodobnější důvod, který nám umožňuje dodržed lhůtu. To je, bohužel, mód, ve kterém skončí většina korporátních programovacích zaměstnání dříve nebo později (obyčejně spíše dříve). A nedělá to nás programátory zrovna šťastnými. Vetšinu z nás, doufal bych. Každopádně já, pokud mám mluvit sám za sebe, chci být na svou práci pyšný, ne se za ní stydět. Je skvělé být schopen udělat něco rychle - a já jsem byl obyčejně (a také naneštěstí) tím, kdo je pověřen uděláním takových věcí - ale je ještě skvělejší dělat ty věci také správně. A to většinou ubírá na té "rychlé" části.

    Samozřejmě, klient většinou ani neví, že produkt pro něj udělaný mohl být mnohem lepší, nebo že jsme byli nuceni vzít to zkratkou, která může vyústit ve sníženou rozšiřitelnost toho systému, který jsmě pro ně udělali. Ale my víme! A to takovou práci dělá, alespoň pro některé z nás, o tolik těžší, abychom vydrželi pracovat dlouhodobě pro nějakou korporaci.

    V případě větších korporací si šéfové dokonce jasně uvědomují, o co nás programátory žádají, ale "Je OK udělat to rychle a špinavě pro teď a příští rok či pololetí budeme mít peníze z nového rozpočtu abychom to celé předělali tak, jak to mělo být od začátku. Teď ale na 'správně' nemáme rozpočet." Někty to opravdu znamená udělat celý ten sofwér úplně od začátku. Obecně to nedává žádný smysl, ale dává ho to v prostředí korporátních rozpočtů. Hádám...

There is a kind of a maxim in programming world regarding time estimation that goes something like this:

V programátorském světě existuje na odhadování délky doby potřebné k vývoji poučka, která zní nějak takto:

First estimate the time needed to implement a feature to the best of your knowledge, ability and experience. Then multiply the time you came up with by 3.
And do that multiplication even if you've already taken this maxim into consideration when creating your original estimate to start with.

Nejdříve odhadni dle své nejlepší zkušennosti délku času, který bude potřeba k implementování určité funkce sofwaru. Pak tento odhad znásob třemi.
A to znásobení proveď i v případě, že jsi měl tuto poučku na mysli už při dělání svého původního odhadu.

And it's true most of the time. (That is if you want to keep all the planned features and implement them correctly with future in mind*.) Most programmers usually get much closer in their estimate to the actual time that turns out to be needed to implement a new feature if they do multiply their original estimate by 3, however ridiculous that sounds even to themselves.

A toto většinou opravdu funguje. (Tedy pokud chce programátor dodat všehny plánované funkce a implementovat je správně, s budoucností na mysli*.) Většina programátorů se obyčejně přiblíží mnohem blíže v jejich odhadu ke skutečně potřebnému času na implementaci, když opravdu znásobí svůj původní odhad třemi, jakkoliv šílené se jim to samotným může zdát.

* You should further know that once a piece (any piece) of code is written, that does not mean it is done and nobody will ever have to touch it again. It is actually quite likely that any code will be continuously improved and extended to serve newly added features and before ignored or unknown requirements, as well as to improve its clarity. If it is written well, this is much easer to do. And writing code with that in mind also makes the whole process longer as one has to always think about whether it is clear what the code is doing for when other programmers or even yourself might be reading it again in the future, maybe because it needs to be edited.

* Měli byste dále vědět, že jakmile je nějaký kus (jakýkoliv kus) kódu napsán, neznamená to, že je hotový a nikdo nikdy už se ho nebude muset znovu dotknout. Je naopak velmi pravděpodobné, že jakýkoliv kód bude průběžně vylepšován a rozšiřován, aby nabízel nově přidané funkce a dříve ignorované nebo neznámé požadavky, ale také aby byla zvýšena jeho čistota a čitelnost. Když je napsán dobře, je toto mnohem jednodušší dělat. A psaní kódu s tímto na mysli také činí tento proces delší, neboť programátor musí stále přemýšlet, jestli je jasné, co jím psaný kód dělá, aby v budoucnu jiní, nebo i on sám, mu byl schopen znovu porozumět a byl schopný ho efektivně upravit.

The same problem can be solved in code thousand and one ways, some of which are clearer to read, some of which are quicker to write, some of which are more extensible in future, some of which will execute faster, but may take more memory, others slower but take less memory etc. and all this needs to be considered. And that does what? It again can extend, but sometimes also shorten, the time needed to deliver the result.

Ten samý problém je možné v kódu vyřešit tisíci a jedním způsobem, z nichž některé jsou jasnější na přečtení, některé jsou rychlejší na napsání, některé jsou více rozšiřitelné v budoucnosti, některé budou počítačem vykonávány rychleji, ale mohou potřebovat více paměti, jiné pomalejší, ale s nižší potřebou paměti atd. A všechno toto musí být zváženo a zvolen ten nejvhodnější přístup k danému problému. A to co? Zase jen dále prodlužuje, ale někdy naopak urychluje, dobu potřebnou na doručení výsledku.

Programming is an engineering job and as such involves lots of decisions about what property of the final product is more important than another as implementing both may be physically impossible. Think for example power versus size of a motor. It would be ideal if a motor would have unlimited power and was infinitely small at the same time, but that is clearly not possible. We need to know how much power we need and that will result in a motor of a certain size and weight. We can make it lighter by using more expensive, but lighter materials, but then we may be also limited by the maximum tolerable cost. We may also make it smaller if we come up with some new, maybe never-tried-before solutions. Then we are risking its reliability. Maybe we can make it smaller by just using thinner wire for its coils but in that case its longevity may be severely negatively affected. But maybe that's fine for the use case for which it is needed. Engineering is all about balancing such conflicting requirements. And software engineering is no different.

Programování je inžennýrská práce a jako taková obsahuje velké množství nutných rozhodnutí o tom, jaká vlastnost finálního produktu je v daném kontextu důležitější než jiná, protože není fyzicky možné implementovat obě zároveň. Můžeme se o to jen snažit. Představte si například sílu motoru versus jeho velikost. Ideální by samozřejmě bylo, kdyby motor měl nekonečnou sílu a byl zároveň nekonečně malý. My ale v reálném světě musíme předem vědět, jak silný motor potřebujeme a to se promítne do toho, jak velký a jak těžký bude. Můžeme ho udělat lehčí tak, že použijeme lehčí, ale dražší materiál, ale zde zase můžeme být limitování maximální přípustnou cenou. Také se můžeme pokusit ho udělat menší, pokud použijeme nějaké nové, nikdy předtím nevyzkoušené řešení. Pak riskujeme jeho spolehlivost. Možná ho můžeme udělat menším, když (u elektromotoru) použijeme hubenější drát na jeho cívky, ale v tom případě může zase být ohrožena jeho dlouhověkost. Ale možná to je v pořádku pro to použití, pro které má sloužit. Třeba ostatní součásti toho celku vydrží beztak ještě kratší dobu. Inženýrství je celé o balancování takových protichůdných požadavků. A softwérové inženýrství není v tomto odlišné.

So we must constantly ask ourselves lots of questions that we need to find the best possible answers for. We have to devise solutions that must often sacrifice some part of one ability in order to gain on another, more important aspect. You can't have everything in life and you can't have absolutely everything in a computer program either. But you can try to get as close as possible. And that is hard.

Takže sami sobě musíme stále pokládat spousty otázek, pro které musíme nalézt ty nejlepší odpovědi. Musíme přicházet s řešeními, která musí často obětovat část jedné schopnosti aby se posílila jiná, důležitější schopnost. Není možné mít vše v životě a není možné mít absolutně všechno ani v počítačovém programu. Ale můžeme se pokusit dostat se tak blízko, jak to jen jde. A to je těžké.

It is not unusual to see me just looking at the screen for extended periods of times. Sometimes even hours. And it's not just me. It does not mean that I'm not working. In fact I'm probably stretching the capabilities of my brain to the limit trying to design things in my head, doing virtual tests and trying to figure out the best way forward, attempting to consider unspecified future needs. Some people may not do that and those are usually the "not so good" programmers who often produce code that is very confusing, unreadable and complicated and the product for the user is much less useful if not straight away terrible. In the end, spending a couple of hours just thinking through a problem properly more often than not can save many more hours, or even weeks or month in the future on writing, extending and maintaining badly written code.

Není úplně neobvyklé vidět mě pouze hledícího do obrazovky po docela dlouhou dobu. Někdy i hodiny. A to nejsem jen já. Neznamená to ale, že nepracuji. Velice pravděpodobně natahuju závity svého mozku až na hranice jeho možností, pokoušejíc se designovat věci v mé hlavě, provádějíc virtuální testování a zkoušíc nalézt tu nejlepší cestu kupředu, pokoušejíc se brát v úvahu i nespecifikované budoucí potřeby uživatele / zákazníka. Někteří programátoři toto možná nedělají a to jsou právě většinou ti "ne tak dobří", kteří často vyprodukují kód, ktrý je matoucí, nečitelný a komplikovaný a výsledný produkt pro uživatele je mnohem méně použitelný, pokud není vyloženě hrozný. Ve výsledku je lepší strávit pár hodin hlubokým přemýšlením o problému, než se pustit bezhlavě do chrlení co největšího množství zmateného kódu. Často těch pár hodin čistého přemýšlení ušetří v budoucnu mnohem více hodin, nebo kolikrát i týdnů či dokonce měsíců práce při psaní, rozšiřování a udržování špatně napsaného kódu.

And, then there is something called "scope creep". Those are the hundreds of little or even more significant features that neither the client originally realized they wanted nor the programmer realized would be "nice", or helpful or actually necessary to implement in the program.

A pak je tu něco, čemu se anglicky říká "socpe creep". Přeložil bych to asi něco jako "nabalování požadavků". Těch stovek různých malých nebo i podstatnějších vlastností, které si ani zákazník původně neuvědomil, že chce a ani programátor, že by byly "příjemné", nebo nápomocné, nebo dokonce nezbytné, aby byly do programu implementovány.

Being able to keep the project scope strictly only to features originally intended is therefore also hard. But it more or less always has to be done. However, it probably never can be done 100%. Some features just simply must be there if we don't want to look like amateurs, regardless of the fact that they were not in the original scope of the project.

Tudíž být schopen držet rosah projektu striktně na pouze těch funkcích, které byly zadány v původním záměru, je také velice těžké. Ale více méně to musí tak být. Avšak téměř nikdy to tak nemůže být na 100%. Některé funkce prostě a jednoduše implementovat musíme, pokud nechceme vypadat jako amatéři, přestože nebyly udány v původní specifikaci.

There is probably yet more that actually comes into play (like internationalization = translation to other languages, programmatic testing, or in our lingo test-driven-development, where we first program tests for the code we intend to write etc.) but these are some of the things I remembered from the top of my head and felt I could explain to a lay person. Hopefully I managed to achieve that goal to a good degree.

Těch věcí, co děljí z vývoje softwéru běh na dlouhou trať je pravděpodobně ještě znatelně více, než co se mi vybavilo při psaní tohoto článku (jako třeba internacionalizace, tedy překlad sofwéru do dalších řečí, test-driven-dvelopment, neboli psaní samotného programu až po napsání testů, které musí splňovat atd.), ale to co jsem napsal jsou aspekty, které se mi vybavily a které jsem cítil, že snad jsem schopen vysvětlit nějak i lajkovi. Snad se mi to alespoň trošku povedlo.


And there you have it - a bit better idea about what us software developers actually do and have to deal with. Let me know what you think if you feel like it.

A tady to máte - trošku lepší představa o tom, co my softwéroví vývojáři vlastně děláme a s čím se musíme ve své práci vypořádávat. Dejte mi vědět, co si o tom myslíte, když se vám bude chtít.


Just by the way, this very page, as I'm looking at it now, has almost 2 500 lines of code and text if I subtract the presented final code and documentation lines for the presented component itself. However, each paragraph of this text is only counted as one programming line, so the actual length comparable to an article, or a book, would be much longer. Trying to copy and paste it into a Word document results in 109-page long document :). Well, it is also written in two languages, so that makes it significantly longer.

Jen tak mimochodem, tato stránka samotná má, jak na to teď tak koukám, téměř 2 500 řádků kódu a textu, když odečtu ty řádky prezentující kód té mé komponenty a řádky té dokumentace k ní. Avšak navíc každý odstavec textu je počítán jen jako jeden jediný řátek kódu, takže opravdová délka srovnatelná s jiným článkem, nebo spíše už knihou, by byla značně delší. Zkusil jsem zkopírovat a vložit HTML obsah této stránky do Wordu a tam to vychází na 109 stránek dlohý dokument :). No je také psaný na této stránce ve dvou řečech.