Newer
Older
** 2017-01-28 关于 rpn calculator 的心得
- 第一点,编写小Web组件很费时间,尽量不要发明新组件和展示方式。让Google和Yahoo为
你的开发提速。使用他们测试过的组件来展示你的程序的内在逻辑。
Title with button on the right. 应该有现成的组件可以做这个功能。比如 toolbar,
action bar。
- 第二点,当界面足够复杂,交互比较多的时候,不要妄图移动和桌面用同一份HTML。
单独为移动设计是正路。mobile first design.
** 2017-01-28 visual design notes
- desktop
- #container
width: 700px
margin: 10px auto;
- #stack
width: 200px
- #keyboard
width: 300px
- #trail
width: 200px
- mobile
- #container
width: 300px
- #stack
width: 300px
- #keyboard
width: 300px
- #trail
width: 300px
** 2017-01-27 steps to add new operator button. :doc:
- add button in UI HTML.
- update fsm.js keyNames, shortKeyNames, operatorKeyList.
- add operator handling code in fsm.js.
If you can use this.doUnaryOperator() or this.doBinaryOperator(), use that
is easier.
General steps:
- take snapshot.
- manipulate numberStack.
- if there is error, call this.setErrorMsg(), pop snapshot and do early return.
- if there is no error, push result in numberStack, push new op and result to this.trail.
- add event handler in calc.html.
set data-bt-name on td.
if there is a hotkey, handle that hotkey. just update keyMap map.
** 2017-01-26 design data structure for the calculator.
- this can be written and tested independently from the web UI.
- basic data:
- stack of numbers.
- current state and data in finite state machine (fsm).
- data for undo. I think I can just take a snapshot of the fsm.
- how many states are there?
| this state | event | next state |
|------------------------------+---------------------------+--------------------------------------------------|
| idle | press num keys or dot key | modify number, then waiting for number or action |
| idle | press +-*/ swap | do op, then idle |
| waiting for number or action | press num keys or dot key | modify number, then waiting for number or action |
| waiting for number or action | press return key | commit number, then idle |
| waiting for number or action | press +-*/ | commit number, do op, then idle |
| waiting for number or action | press < key | modify number, then waiting for number or action |
| idle | press return key | dup number, then idle |
| idle | press < key | drop number on stack, then idle |
| idle | press undo | restore to previous snapshot, then idle |
| waiting for number or action | press undo | commit number, then do undo, then idle |
There is no ending state, the machine can always accept new keyboard events.
- a few display and error handling.
- when number is "0", press more num0 will just show error and not modify the number.
- when number is "", press dot will change number to "0."
- when number already has a dot in it, future dot will just show error and
not modify the number.
** 2019-02-17 how to deploy to production?
https://emacsos.com/calc/
fab deploy
** 2017-01-28 check why it runs slow in firefox mobile on android. :performance:
try type digits 1 to 9, not all numbers get accepted.
it has no problem in chrome.
** 2017-01-28 try convert it to native app using phonegap.
I like HTML5 offline website better than phonegap app.
I prefer either native app or real website.
** 2017-01-27 deploy on https://rpn.emacsos.com/
- I considered https://www.emacsos.com/rpn-calculator.html, but I don't want
to mess up with sw.js scope issue.
maybe use calc-sw.js? I still don't like that.
- just use a standalone domain.
- add link on www.emacsos.com.
- write a blog post about it. techs used in the project. and source repo.
- deploy notes
create ./fabfile.py
create ~/projects/salt/rpn-emacsos-com/init.sls
create ~/projects/salt/rpn-emacsos-com/cert.sls
create ~/projects/salt/rpn-emacsos-com/rpn.emacsos.com.conf
Can I reuse the same cert from www.emacsos.com? I need to regenerate cert.
No wildcard cert is not convenient.
TODO create SSL cert for rpn.emacsos.com
- DONE fill in link for #TODO link in about page.
- add LICENSE info and source code repo address in about overlay.
** 2017-01-27 how to capture backspace key in javascript? what about TAB key?
** 2017-01-27 the whole FSM can be made persistent across sessions.
not only trail.
- load FSM data on page init.
- save FSM data on each call to FSM functions.
The usefulness is doubtful. I will leave it.
** 2017-01-26 make the whole thing a polymer web component
<rpn-calculator trail="true"></rpn-calculator>
User can also set height and width on the element, which will resize the
elements inside. When there is no room, show a scrollbar.
shadow dom should make all HTML/CSS/Javascript work together without affecting
the parent DOM.
* current :entry:
**
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
** 2017-01-29 design a new UI for mobile. The emacs calc UI is not designed for mobile.
Try copy from huawei built-in calc.
[ ]
[ ]
[ > ]
C / * <
7 8 9 -
4 5 6 +
1 2 3 ret
ret
0 U . ret
- no scrolling at all.
- stack display is 3-4 lines.
- display size
nexus6p 412x732
nexus5x 412x732
iphone6 375x667
iphone6p 414x736
iphone5 320x568
- make my UI auto adjust itself according to a container size.
container size is set to 98vh and 98vw.
- left slide show full stack window.
right slide show full trail window.
like wordpress previous post and next post gesture.
-
** 2017-01-27 add some visual feedback when clicking a button. like in material design.
** 2017-01-27 UI problem: when input a very large number, #number-display and keyboard will grow.
should set a max-width on #number-display.
overflow: auto
doesn't work with <td> in firefox.
* done :entry:
** 2018-11-13 calc bug
reproduce: press 1, press RET, press 2, press DEL, press Plus, Calc show
result NaN. Expect 0 being pushed to stack.
When use press DEL, number value should be zero on display and when pushed to
stack. Also press 2, DEL, 3, display should show 2, 0, 3.
- 2019-02-17 add failing test case.
fix the issue.
make sure all tests still pass.
** 2017-01-28 try it on mate8.
I can transfer files using bluetooth.
- problems
- mate8 File app can't extract tar.gz. try zip file.
- when I click on html file, there is no open by chrome choice on android. why?
- html file runs slow in firefox mobile.
touch button feels very slow. not a good UX at all.
landscape mode fails badly when I don't hide location bar. I can't even
see return key.
- try copy URL from firefox to chrome.
paste URL in chrome works.
it runs much faster in chrome. touch keyboard button is just like native app.
** 2017-01-28 UI on mobile device is not good.
- I can't see top 3 number on stack.
I can see it when location bar is hidden.
- make title line smaller, use slide in panel/tab for buttons and detail views.
hide trail by default.
move stack to top.
- swap - and return location
- I think it's good enough on chrome mobile
** 2017-01-28 UI tweaks
- DONE about page, HELP should be centered. change "Close" to "Return to calc".
put back button on the rhs.
- DONE about page, add font-family config.
- DONE div title should put buttons across the row. // now links/buttons are
put on the rhs.
see ./test-rhs-link-on-title-row.html
-
- DONE "refresh page" is for debugging purpose, hide it somewhere.
- DONE add about dialog. could put "refresh page" in this page. hidden by default.
- test it in mobile mode.
- make scroll work on desktop and mobile when window height is small.
position: fixed probably won't work.
move overlay to <body>. use slide effect.
hide #container when showing #help-overlay-container and vice verse.
I don't need the dark effect and click to go back event handler.
see example in ./test-div-slide-in-and-out.html
- implementation notes
- how to implement a pop up window (overlay)?
see ./test-div-overlay.html
how to cast a shadow to make it more like a window?
~/html/yxyAR/src/static/common.css
box-shadow: 2px 2px 2px black;
- move "Help" to center. move "close" button to right.
** 2017-01-27 add an about dialog.
add "about" button beside "Reset All".
- show what is RPN calculator, how to use this program, author, program
version and source code location.
-
** 2017-01-26 make it work offline, add sw.js
- cache calc.html, fifo.js, fsm.js.
try update cache when cached page is more than 1d old.
- cache vendor/* indefinitely.
do not check for update.
I think I can use version to handle this when I do upgrade vendor assets.
- implementation notes
- see previous example in phonebookhs project.
~/projects/phone-book/phonebook/static/js/sw.js
** 2017-01-27 bug: ** is not supported in firefox.
use Math.pow() instead.
** 2017-01-27 accept keyboard event as well. :low:featurereq:
- num0 to num9
- dot
- n for change-sign
- enter for return
- backspace
- +, -, *, /
not sure about
shift+= => +
shift+8 => *
- tab for swap
- This is only useful on desktop. Not very useful on mobile.
Can I skip a JS code block when executing on mobile browser that doesn't
have physical keyboard?
- problems
- backspace doesn't trigger .keypress().
- tab jump between links. doesn't trigger .keypress().
** 2017-01-27 add a button: sum-all, it will sum all numbers on stack. :featurereq:
- make UI auto height based on keyboard height.
- add three buttons: sum-all, square-root, power
** 2017-01-27 add a button: sqrt :featurereq:
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
** 2017-01-26 make undo work
- action based or snapshot based?
- how does "undo" fit in the FSM states?
it's an op, like swap/return.
what can undo do?
undo commit a number. (this just undo a return misc command)
undo an op. (restore stack)
it will not undo backspace when in "waiting for number or op" state.
- how to implement it?
init FSM:
this.snapshots = new fifo.BoundedQueue(100);
on FSM op handler:
this.snapshots.push(this.save());
//do op
on FSM "undo" op handler:
this.load(this.snapshots.pop());
when user undo, show "Undo!" in error msg.
if this.snapshots.pop() return undefined, undo fail with error msg "No more undo history!".
It can undo the two "clear" and "reset" commands as well. Just take snapshot
before running those.
- undo does not write undo op to trail. it unwind the whole FSM.
in emacs, undo doesn't change trail at all.
should I keep trail data in this.save()?
I will just restore trail to previous saved snapshot.
- can I undo a undo? no.
s0, e1 => s1
s1, e2 => s2
s2, undo => s1
s1, e3 => s3
s3, undo => s2
s2, undo => s1
s1, undo => s0
DONE add unit test for this.
- problems
- when to take snapshot?
num1
return {[]} [1]
num2
return {[1]} [1, 2]
undo
my initial design has no problem. do it before each op.
- save snapshot doesn't work.
when commit number to stack,
r = this.save()
r is undefined.
r["numberStack"] is a mess. not an array at all.
is it about the waiting state?
when doing the push, the current state is still waiting state.
do it before modifying stack instead. I don't want to take a snapshot on waiting state.
- it's a shadow copy problem. save() returns a reference to the same
array. that will create lots of problems. I need a copy there.
see example in ./test-shadow-copy.html
how to clone array in javascript?
use .slice(0) on the array.
- I see the problem.
undo should be FILO stack, not FIFI queue!
I need a bounded stack.
** 2017-01-26 make basic things work.
- DONE draw the keyboard using HTML and CSS
- DONE make basic number input and arithmetic work
- DONE make DUP and SWP work
- DONE make trail work
** 2017-01-26 make trail persistent
trail is a bounded fifo queue, it just store recent 1k entries.
when multiple instances of calculator is working. they all write to a single
trail cache. it's not a problem.
** 2017-01-27 make trail work
- when is trail being updated?
- when a number is committed to number stack. return.
- when a new number is pushed to stack as an op result. plus, change-sign.
- this should be part of the FSM.
what data should I store?
it's a queue of [(Maybe op, number)].
as mentioned previously, this data can be made persistent across
pages/sessions.
- DONE first write a bounded fifo queue in javascript.
- load persistent data for trails when init the FSM.
// is updating local storage a heavy operation? I hope it doesn't sync to
disk everytime I update a key.
- implementation notes
- show last 10 entries in UI. allow user to use scrollbar to scroll to
earlier entries.
leave this UI optimization later. for now, just show last 10 or 20 entries.
- use table to show trail in HTML.
trailOp right align.
trailNum left align.
mark last entry using color highlight. there is no need for > marker.
- grep for "this.numberStack.push" to push elements to this.trail.
- change-sign op is not shown in trait. why?
it's because shortKeyNames key should be key name string literal, not the
js constant identifier.
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
** 2017-01-27 design a layout for mobile.
- layout on mobile:
stack
keyboard
trail
- stack and trail has smaller height as well.
- how to test it on device?
I don't have wifi.
try use AP mode.
mate8 phone can't access the IP on x201.
maybe there is no route for 192.168.43.54/24.
- implementation notes
- 200+300+200=700
apply css when width < 720.
width: 375, 412, 414
- TODO test it in portrait and landscape mode.
does css auto adjust page when user rotate phone?
landscape mode height need to be adjusted.
try not to use fixed pixel in div height/width.
DONE add expand button on stack and trail.
when expand, set max-height to 300px;
default max-height is 100px;
- keyboard should be put on top on mobile device.
I don't want the keyboard to move down when stack grows.
I remember flex has an attribute that support this.
apply on the children.
css order property. it's an integer.
works perfectly.
-
** 2017-01-27 add a reset button. browser refresh button is not always easily accessible, esp on mobile.
** 2017-01-27 stack, trail: overflow problem.
- when there is not enough room, show scrollbar.
- always show the title section.
- auto scroll to bottom when content overflow.
TODO how to scroll to bottom?
$('#stack-content').scroll
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
** 2017-01-26 make < (backspace) work.
** 2017-01-26 make stack auto numbering work
** 2017-01-26 handle error for every this.numberStack.pop()
- handle error for every this.numberStack.pop()
- show lastError in web UI.
RPNCalculator should clear lastError when it processed another sendKey()
successfully.
maybe do early return whenever I called this.setErrorMsg(errMsg).
- implementation notes
- [].pop() return undefined when array is empty.
- when fetch 2 values, if only 1 fail. the pop() number should be put back
to the stack. add a unit test for this.
- about division by 0. js just return infinity.
I will show error instead and do not consume any number on stack.
** 2017-01-27 feature: backspace key, when idle, should delete number on stack.
** 2017-01-27 bug: press number-display button trigger an error.
it is treated as a num key.
the FSM should only accept known keys.
update isNumberKey().
** 2017-01-26 make basic number input and arithmetic work
- DONE make event work.
- MOVED add some visual feedback when clicking a button. like in material design.
- DONE design data structure for the calculator.
- DONE add unit test for the data structure and functions.
- DONE make number input and simple calculation work.
** 2017-01-27 make RPNCalculator work with web UI.
- make event handler work with RPNCalculator.
- sync this.numberStack and this.currentNumber with the web UI.
** 2017-01-26 what to do with negate operator?
emacs use n (change sign)
add this button. move divide button to somewhere else.
** 2017-01-26 how to write a fsm simulator in javascript.
- try write an easier example before writing the real thing.
- easy example:
check whether a string has substring "abb".
states:
| this state | event | next state |
|------------------+-----------------------+-----------------------------------------------------------------------------------------------|
| wait for "a" | read new character ch | if ch is "a", wait for 1st "b"; otherwise, wait for "a" |
| wait for 1st "b" | read new character ch | if ch is "b" => wait for 2nd "b"; if ch is "a" => wait for 1st "b"; otherwise => wait for "a" |
| wait for 2nd "b" | read new character ch | if ch is "b" => succeed; if ch is "a" => wait for 1st "b"; otherwise => wait for "a" |
| succeed | ∅ | ∅ |
- it works.
** 2017-01-26 draw the keyboard using HTML and CSS
** 2017-01-27 feature: long press backspace just delete the current number.
there is no long press mouse event.
won't add this feature.