应用 (app) 的技术周期 – 词焙的重构实践

作为造轮子专业户, 自己的一些小东西不可避免地重写了一次又一次, 不过在应用层面, 我接触到的架构上的重新设计案例并不多. 几个月之前, 我结束了词焙 0.x 版本的开发, 开始重新设计并实现 1.0 版本, 是一次有意思的经历.

应用的技术周期

对于词焙 (web app) 来讲, 应用从最初概念的提出, 需求设计, 代码实现, 到未来逐渐成熟, 我想会经过如下几个阶段.

  1. 最初的需求实现.
    这个过程中, 会有一个针对的架构设计, 这个设计或许满足最初的需求实现, 也考虑了扩展能力, 但由于思维的局限性, 通常会遗留一部分问题.
  2. 不断补充的需求与对应的实现.
    此时, 会暴露一些原有细节实现的问题, 但更棘手的是, 可能会发现原有架构难以满足进一步的改变了.
  3. 架构的重新设计与实现.
    在应用上线一段时间后, 将暴露的问题和新增的需求纳入考量, 重新设计应用架构.
  4. 新架构实现的重构.
    在实现新架构某一部分的过程中, 分析现有技术实现遇到的问题, 并进行这部分实现的重构.
  5. 接受新需求与对应的实现的检验.

目前词焙 1.0 处在第三个阶段, 并且已经发现了一些组件设计中不完善或者不合理的地方, 会在未来进行重构.

新版词焙的架构考虑

老版本的词焙设计之初, 是需要联网使用的, 但由于用户的强烈要求, 不得不将本来为联网使用设计的简单缓存结构硬改 (其实是重写) 为支持离线和同步的结构, 很多实现非常生硬, 难以维护. 另外在 UI 这一块儿, 一开始因为进度需要, 没有做好规划, 触摸手势及相关的事件管理也没有做好统一, 这些都成了新版词焙设计时重点考虑的地方.

使用 TypeScript

前后端全面改用 TypeScript 开发, 静态类型检查与对应的自动完成很能提高开发效率.

基于 Promise (ThenFail)

刚开始接触 Promise 的时候其实有点抵触, 但后来发现上手了也挺好用的. 但本着造轮子的精神, 写了自己的 ThenFail, 使用 TypeScript 开发, 也可以更方便地添加需要的 helper.

离线数据与同步 (Syncable, 面临重写, 未来将整理开源)

除了简单的数据同步外, 词焙中需要一个可以累积的东西以储存类似每天学习时间的数据. 但考虑到未来或许还会处理更复杂的数据合并, Syncable 为这一块儿预留了相关接口. 为了进一步增加该组件的一般性, 还添加了诸如只读数据, 被动 (按需) 同步等特性.

触摸与手势 (Touch Delegate)

上一版词焙实际上也使用了类似 Touch Delegate 的做法, 但是不支持多触点, 并且缺乏终止事件传播的机制 (因为没有统一的管理), 这些是重写这部分的原因. 与此同时, 保留了之前 Gesture Identifier 的模式, 方便手势扩展.

MVC 框架 (Drop, 面临重写)

相对 ThenFail 这么个 Promise 实现来讲,,, 自己搞一套 MVC 框架确实需要点超越时代的轮子精神在里面, 但也算实现了个愿望, 因为还在 2011 年的时候就一直想写这么个框架. 考虑到开发词焙的时间不是特别充裕, 但又耐不住想要写这么个东西, 这次实现的时候调整了原有的模板语法, 力求实现简单, 同时也尽量保留原有设计的精髓 (一般性). 但感觉歪打正着, 现在反倒更喜欢这个实现简单的语法, 而一般性也得到保留 (具体可以参看 Drop 中各种 Decorator 的实现).

不过将 Drop 应用到词焙开发上之后, 还是遇到了不少问题. 其中一些问题通过添加 Decorator (修饰器) 的形式巧妙地解决了, 另一些则还有待框架本身的改进, 比如对象字面量的支持 (实现时为了解析简便, 不允许为嵌套未转义的 { }), 对表达式的解析.

服务器角色的转变和重构

目前来讲, 词焙的服务器基本就是作为数据同步中心存在的 (其实之前改离线之后就已经是这样的功能了). 同时, 新的服务器端脚本也使用了 TypeScript 和 Promise, 结构更加清晰, 实现也更加优雅.

总结

总体而言, 这次架构的重新设计自己还是比较满意的, 也产出了几个实用并且通用的组件. 我想之后会把这些经验和组件打包一下, 做成一套 web app 的解决方案. 当然, 在此之前, 还得完成具体实现的重构, 并且经得住需求的考验.

Understand Visual Studio Tools for Apache Cordova

Recently I turned to Cordova for building a new version of my app, as I am a Visual Studio user, it’s great to get an official tools from Microsoft for Cordova.

Cordova is a tool for building web applications running in native shell. Due to the nature of web apps, and the effort make by Cordova team and its ecosystem, Cordova is also a great tool to build cross-platform apps.

Actually, Cordova also provide a way to develop web apps dedicated to a specific platform. Though that won’t be discussed in this post.

For building cross-platform web apps, refer to http://cordova.apache.org/docs/en/edge/guide_cli_index.md.html

After creating a Cordova project, a “www” folder will be created with some initial files. The major project usually goes here. And there’s also a “platforms” folder that holds the project files for each platforms.

However, once a specific platform is added to your Cordova project, Cordova will leave most of the files as they are. This means that you may actually open a specific platform folder as a project in your IDE, edit and debug them.

Though these “most of the files” does not include cross-platform contents, e.g. contents in folder “www”.

The situation for plugins is quite the same, after the installation of plugins, Cordova will not touch the files that have already been generated, even if they’ve been changed by you.

This is what’s going on with Cordova CLI, however, not Visual Studio Tools for Apache Cordova.

Let’s put the directory structures of original Cordova and VS Tools together and we’ll be able to understand the differences.

Cordova CLI
- project
  - platforms
    - wp8
      - www
  - plugins
  - www
  - config.xml

VS Tools
- project
  - bld
    - Debug
      - platforms
        - wp8
          - www
      - plugins
      - www
      - config.xml
  - plugins
  - config.xml

VS Tools treat the entire project folder as “www” except for some special ones for Cordova. But after all it’s a tool box for Cordova, what’s their connection?

Basically a project defined by Visual Studio Tools for Cordova is not a real Cordova project, but a project that this tool will manage to convert to Cordova projects based on your configuration.

So what you see in bld\Debug\ (or Release) folder is the actual Cordova project.

I mentioned that Cordova won’t touch most of the files once they are generated. This good, but sometimes it will bring problems (e.g. install then uninstall plugins). However, the way Visual Studio Tools use brings also problems, and maybe even more…?

For example the installation of plugins does not support variables, and editing native projects is really painful.

But hoping they will solve these problems soon in the future when it goes to RTM.

Checkout Visual Studio Tools for Apache Cordova (CTP3).

Cordova 微信分享插件 (iOS, WP, Android)

先上 GitHub 链接 https://github.com/vilic/cordova-plugin-wechat

因为词焙需要, 然后现成的没有好用并且平台覆盖全面的, 于是就参考已有的自己写了个.

没有具体去看最低支持到 Cordova 多少, 但 3.x 应该问题不大. PhoneGap 估计也差不多.

支持三大系统, 除了安卓外, 基本不用额外配置, cordova plugin add com.wordsbaking.cordova.wechat –variable APP_ID=[你的AppID] 之后相关配置 (包括 URL Scheme) 都弄好了.

分享的内容只支持文本, 图片和链接. 以后空了再增加其他支持.

安卓需要额外修改一下 plugin.xml, 具体的参见 GitHub 上的说明.

{{Angular}}, {{Ember}} or {Drop}?

Okay, this post is actually not about comparing these three frameworks. But to introduce my DropJS.

Though actually I can’t compare DropJS to the others, even if I want to… Because it just can’t. But it exists for reasons. For example: why not {single braces} instead of {{double braces}}; why lacking of a gentle escaping notation; why conventions, especially why conventions on strings and letter capitalization…

I had some thoughts on MVVM framework years ago and actually tried to implement DropJS years ago. Sadly I didn’t make it. One of the reasons is that the template style I used was not very friendly to parse (though I do remember I had written a parser successfully, then maybe it was because other reasons). As for Drop, it’s clear and simple. To make everything easier, I come up with an idea called “decorators”. It’s somewhat like the concept of “directives” in Angular based on my understanding (though not very good understanding), but written in a way of {handlebars}, single-brace {handlebars}. 😉

Some code:

<!-- AngularJS -->
<input type="text" ng-model="name" />
<h1>Hello {{name}}!</h1>

<!-- EmberJS -->
{{input type="text" value=name}}
<h1>Hello {{name}}!</h1>

<!-- DropJS -->
{%var name}
{%bind-value name}
<input type="text" />
<h1>Hello {name}!</h1>

I have to say that the code written in Drop is a little bit more than Angular and Ember. But actually {%var name} can be removed if {name} exists in the model. Or you can just write your own {%bind-value} decorator with the ability to define variables under the scope. Because even {%var} is just a normal decorator.

You may want to check out the source code of these decorators here lib/decorators.ts. Yup, Drop is written in TypeScript.

GitHub https://github.com/vilic/drop
Demo https://rawgit.com/vilic/drop/master/demo/index.html

TypeScript or Pure JavaScript?

I was really excited when I got to know that Microsoft had released a new language called TypeScript.

Type, which means better intellisense and type safety that serve the purpose of its existence — scalable.

Why is this important?

  1. Type safety.
    Hate bugs? There’s a joke we talk a lot, which is also a truth: “The weirder the bug is, the stupider mistake we might have made.” And the compiler would now worry about some of these things for us, like typo or forgetting to change a property after copying some code.
  2. Tooling.
    When a project grows, the API might become too complex for the developer himself to remember. Visual Studio provides great experience on JavaScript intellisense by actually running the code in background. But on the one hand, it would usually requires a special rewritten-version of intellisense file. On the other hand, it would become very slow when code lines add up. (I wrote a framework called VEJIS which picks up the powerful intellisense feature of Visual Studio, provides support for classes, interfaces, delegates, etc. But it’s a runtime framework and there’s no type safety. Checkout http://vilic.github.io/vejis/)
    TypeScript makes code navigation much easier and most of the time, we won’t even need to navigate because the information we need is there with intellisense.

Searching articles related to TypeScript would result in many that are comparing it to CoffeeScript and Dart. It’s really weird because some (maybe most) of these articles treats TypeScript the same sort of thing as CoffeeScript simply because both of them compile to JavaScript. And I have even seen an opinion saying: “If you want to build a big application I’d recommend going with CoffeeScript as you end up writing less code.”

WHAT? Maybe that buddy haven’t really met something big enough.

CoffeeScript won’t do things better for people like me, who have been writing JavaScript since day one. People like to talk about the “good part” when it comes to CoffeeScript, but… does it really take more effort for one person who starts with JavaScript to remember how to avoid the bad part than learning a syntactically brand new language?

And actually I have never given the “good or bad part” thing much credit.

So back to the topic, TypeScript or pure JavaScript? Ignoring things other than the languages themselves, I go with TypeScript without hesitate. Because it’s a superset of JavaScript, and it provides much more useful features for larger-scale applications. And, the key point, I write relatively large-scale web applications. However, to use TypeScript in productive environment, there would be more to think about.

  1. Poor IDE and editor plugin support.
    There have been several IDEs with TypeScript support integrated. Visual Studio, of course, would be one of them. But the experience coding using Visual Studio can’t even beat the experience on TypeScript Playground. This really bothers me a lot. No automatic-quote/bracket completion, no snippets, strange indent behavior, etc.
    I haven’t tried TypeScript in WebStorm, maybe it would have done better job. Also ReSharper for Visual Studio may improve the experience according to some comments I’ve seen.
    There are also some plugins for Sublime Text, but… you know.
  2. Poor NPM package and cross-project referencing support.
    Actually TypeScript is capable generating definition files so that it should have been friendly to these things. But the reality is not that awesome…
    I have proposed a convention on distributing TypeScript-written NPM package, and hoping it would make things better.

If finally you choose TypeScript, here’s some information and techniques that might help.

  1. Definitions of popular JavaScript libraries.
    I don’t have any crush on open source, but here open source does it right. So far most of the declarations I need for specific JavaScript libraries can be found in DefinitelyTyped, and it’s becoming of better quality and of larger covering.
    You may install the declarations though NuGet on Visual Studio, to make things work after installation, try to refresh the solution tree.
  2. Temporary “best” practice for cross-project referencing in Visual Studio.
    In project properties page TypeScript Build tab, check “Combine JavaScript output into file” (and specify a file name) and “Generate declaration files”.
    Create a “.d.ts” file in parent project and add the declaration file generated by sub project to it as a reference.
    Visual Studio would compile all “.ts” files included in the project into the JavaScript file specified, so make sure other test files are excluded from the project or you may want to create another project for samples and tests.
    When it comes to NPM packages, I haven’t figure out an acceptable way. But following that convention I mentioned earlier in this post, I may try to write an extension for Visual Studio which would pick up declaration files automatically, wrap it up with ambient module name and add the modified declaration file into the project that’s using that module. It would also be useful even if we are using these packages ourselves without publishing it to npmjs.org only. (BTW, symlink would save you hours if you didn’t know.)

I have now a symptom trying to make everything typed, it slows me down starting up coding a project, but speeds my lines up once the skeleton completes. Hope you enjoy writing in TypeScript if you need it.

(BTW it would be great if TypeScript add support for await/async based on Promise.)

A simple URL shorter for NodeJS

It’s called Biu, because in Chinese this Pinyin (something like phonetic symbols) pronounces like “be-yo”, the sound that old-fashioned laser gun would make in films.

I decide not to use database but instead use a single text file, because for a private URL shorter, the links number would hardly be big. And it will read all the links into memory once starts.

Considering its application scene, I think it’s rational to make things, including setup, simpler.

Demo http://biu.link
Github http://biu.link/git (https://github.com/vilic/biu)

Database Solutions for Web Apps

I was working on the offline feature of WordsBaking last month. Considering the data needs to be stored offline may exceed the limit of DOM storage (usually 5 MB as I can find on other websites), I have to pick up a database to handle my needs.

Luckily, there are solutions for different platforms:

  • IndexedDB for Internet Explorer (and other browsers built in Android)
  • WebSQL for Safari (and other browsers built in Android)

But as WordsBaking runs on both Windows Phone and iOS, I need a wrap-up that adapts to those databases. Here’s what I found:

Lawnchair is an extremely lite wrapper with varieties of database adapters built-in, and basically it’s just switching adapters for us. It seems that several people have contributed to this project because the source file, though not big, is written in different code styles… And this is one of the reasons that why I didn’t choose it.

The second reason that made me give up Lawnchair is that it has only a global handler that handles failure events, which will make fallbacks much more tricky. However, it turned out that I did little fallbacks on database errors in the current version as the errors simply won’t occur in normal situations.

PouchDB as explained on its official site, is a project inspired by CouchDB, and . As I am new to NoSQL (maybe new to SQL too), I didn’t know CouchDB much before. But as I was considering using PouchDB as my local database solution, I dug a little deeper and found that it’s something (as sharing the same API of CouchDB) based on revision, and every update needs a revision as parameter and it’s the core of CouchDB.

Actually I even considered replacing current MongoDB with CouchDB as PouchDB is created for offline apps and syncs with CouchDB automatically, but later I realized that CouchDB doesn’t fit the needs of WordsBaking as it updates the records data all the time, which is not the strength of CouchDB.

So while the syncing doesn’t work for me because I have MongoDB on the server, and there’s a surplus “revision” parameter that needs me to handle, PouchDB is out of my little game.

And here comes the hero, YDN-DB. It seems to be a personal project but it’s full-featured and well documented, and it also provides customized code downloading as you may need only some features of this lib. BTW, I noticed it has syncing APIs, but I didn’t get much time to give it a try.

After integrated it with WordsBaking, everything seems to work fine. But as the records growing, initializing of WordsBaking became really slow. The main reason is that I need to read all the study records (maybe thousands) at the beginning. So I wrote a little class to handle this specific situation, to merge these records into a single one (TypeScript), and it works like a charm.

// Edit:
// The code below is written when I had just a little TypeScript and Promise experience.
// And it doesn't look very well right now.

class DBStore {
    name: string;
    keyPath: string;
    mergeLimit: number;

    private merging = false;
    private mergeChanged;

    private merges: { (): void; }[] = [];

    constructor(name: string, keyPath: string, mergeLimit: number) {
        this.name = name;
        this.keyPath = keyPath;
        this.mergeLimit = mergeLimit;
    }

    values = (callback: { (err, values?: any[], mergePendings?: string[]): void; }) => {
        db.values(this.name, null, 1000000)
            .then((values: any[]) => {
                db.get(this.name + '-single', 'values')
                    .then((item) => {
                        var mergePendings: string[] = [];
                        var hash: { [term: string]: boolean; } = {};

                        values.forEach((value) => {
                            var key = value[this.keyPath];
                            mergePendings.push(key);
                            hash[key] = true;
                        });

                        if (item) {
                            var svalues: any[] = item.data;

                            svalues.forEach((value) => {
                                var key = value[this.keyPath];
                                if (!hop.call(hash, key)) {
                                    values.push(value);
                                }
                            });
                        }

                        callback(null, values, mergePendings);
                    })
                    .fail((e) => {
                        callback(e);
                    });
            })
            .fail((e) => {
                callback(e);
            });
    };

    putOne = (value, callback: { (err): void; }) => {
        this.put([value], callback);
    };

    put = (values: any[], callback: { (err): void; }) => {
        if (values.length >= this.mergeLimit) {
            this.merge(values, callback);
            return;
        }

        if (this.merging) {
            values.forEach((value) => {
                var key = value[this.keyPath];
                this.mergeChanged[key] = true;
            });
        }

        db.put(this.name, values)
            .then(() => {
                callback(null);

                db.count(this.name)
                    .then((count) => {
                        if (count >= this.mergeLimit) {
                            this.merge();
                        }
                    })
                    .fail((e) => {
                        alert(e.message);
                    });
            }, (e) => {
                callback(e);
            })
            .fail((e) => {
                alert(e.message);
            });
    };

    merge = (newValues?: any[], callback: { (err): void; } = () => { }) => {

        var hop = Object.prototype.hasOwnProperty;

        var invokeCallback = (err) => {
            callback(err);

            this.merging = false;
            this.mergeChanged = undefined;

            var next = this.merges.shift();
            if (next) {
                next();
            }
        };

        var process = () => {
            this.merging = true;
            this.mergeChanged = {};

            this.values((err, values, mergePendings) => {
                if (err) {
                    callback(err);
                    return;
                }

                if (newValues) {
                    var hash: { [key: string]: boolean; } = {};

                    newValues.forEach((value) => {
                        var key = value[this.keyPath];
                        hash[key] = true;
                    });

                    values.forEach((value) => {
                        var key = value[this.keyPath];
                        if (!hop.call(hash, key)) {
                            newValues.push(value);
                        }
                    });

                    values = newValues;
                }

                db.put(this.name + '-single', {
                    id: 'values',
                    data: values
                })
                    .then(() => {
                        var promise;

                        var changed = Object.keys(this.mergeChanged);

                        if (changed.length) {
                            mergePendings = lodash.difference(mergePendings, changed);

                            if (mergePendings.length) {
                                var keys = [];
                                mergePendings.forEach((id) => {
                                    keys.push(new ydn.db.Key(this.name, id));
                                });

                                promise = db.remove(keys);
                            }
                        }
                        else {
                            promise = db.clear(this.name);
                        }

                        promise
                            .then(() => {
                                invokeCallback(null);
                            }, (e) => {
                                invokeCallback(e);
                            })
                            .fail((e) => {
                                alert(e.message);
                            });
                    })
                    .fail((e) => {
                        alert(e.message);
                    });
            });
        };

        if (this.merging) {
            this.merges.push(process);
        }
        else {
            process();
        }

    };

    clear = () => {
        db.clear([this.name, this.name + '-single']);
    };
}

An Underestimated Tool in Windows: HTA (HTML Application)

I can’t find a convinced source telling in which specific date did HTA become available, but it seems to be earlier than the year 2000.

On the official introduction page of HTA, there’s a highlight saying “The Power of Trust”. Unlike normal HTML pages running in a normal browser, HTA can have almost the same permissions that an exe file could have. Which gives it access to varieties of ActiveX that are not available in normal browsers due to security reasons.

I am always wondering why only a few people know and use HTA? Hmm, maybe one of the reasons is its “Microsoft” tag. I have given up the thoughts hoping HTA could be relatively mainstream, however, for developers who use JavaScript and HTML, HTA could still be a good choice to write some lite productive tools for your colleagues and yourself.

NodeJS would certainly have much better ecosystem now but for some specific tasks I still prefer tools with GUI… T-T (Now I also use NodeJS to do some batch work.)

But it was a shame that Microsoft stopped making it better. Wishes.

Disable Scroll Bouncing Effect of WebBrowser Control on Windows Phone 8

Just another story happens when developing WordsBaking.

First, the basis. If you don’t have any element in your web page that requires overflow style being auto or scroll, “-ms-touch-action: none;” under “html” selector should work well . Actually it works all the time in Internet Explorer, but in a WebBrowser control, if there’s something like a list for which you may need that style, the solution becomes tricky.

I spent tons of hours and tried tons of ways hoping figure out a solution, and luckily, found one.

That is the good news, but the bad news is, this solution doesn’t seem to be elegant (though it works perfectly so far).

The first thing that I found might be useful is that this bouncing effect happens only when your finger touches the elements that already have their contents at the top/bottom or both. So, the first solution I thought might work was to add a pointerdown (MSPointerDown) event listener on the element, and determine whether its content has reached the top or bottom. Unfortunately, it doesn’t work well.

Later I read about an article shows a solution on suppressing scrolling and zooming in WP7 WebBrowser control, I can’t say that it works (on WP8), but it helps.

Combining these two parts (and this is why I think it’s not elegant enough), here’s the solution:

C# Part

private void mainBrowser_Loaded(object sender, RoutedEventArgs e) {
    // here we are using a library named Linq to Visual Tree.
    // http://www.scottlogic.com/blog/2010/03/04/linq-to-visual-tree.html
    var border = mainBrowser.Descendants().Last() as Border;
    border.ManipulationDelta += border_ManipulationDelta;
    border.ManipulationCompleted += border_ManipulationCompleted;
}

void border_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e) {
    mainBrowser.InvokeScript("onmanipulationcompleted");
}

void border_ManipulationDelta(object sender, ManipulationDeltaEventArgs e) {
    var status = mainBrowser.InvokeScript("onmanipulationdelta") as string;
    if (status == "top" || status == "both") {
        if (e.DeltaManipulation.Translation.Y > 0) {
            e.Handled = true;
        }
    }
    if (status == "bottom" || status == "both") {
        if (e.DeltaManipulation.Translation.Y < 0) {
            e.Handled = true;
        }
    }
}

JavaScript Part

window.manipulationTarget = null;

window.onmanipulationdelta = function () {
    if (!window.manipulationTarget) {
        return '';
    }

    var target = window.manipulationTarget;

    var top = target.scrollTop == 0;
    var bottom = target.scrollTop + target.clientHeight == target.scrollHeight;

    return top ? bottom ? 'both' : 'top': bottom ? 'bottom' : '';
};

window.onmanipulationcompleted = function () {
    window.manipulationTarget = null;
};

// and you'll need to make calls to every elements
// with overflow auto or scroll with this:
function preventBouncing(ele) {
    // using jQuery.
    ele.on('MSPointerDown pointerdown', function (e) {
        window.manipulationTarget = this;
    });
}

And good luck fellows~

Thoughts after Developing WordsBaking

Several months ago I started my own project called WordsBaking, which is a tool developed for helping students or learners like me to remember new English words. Considering the time and resources I have, I finally decided to use HTML/CCS/JS technologies so that it could be easier to have the versions for the three major mobile platforms.

But there would certainly be some subsequent issues. In last post, I wrote something about performance, gestures and preventing tap highlight. And in this post, I am gonna write some notes about browser bugs.

First of all, I like IE 10 Mobile quite much, comparing to original Android browser. But unfortunately, it still gets some weird bugs.

1. Scrolling issue with CSS3 transform.

I spent a lot of time locating this issue, as scrolling should be one of the most basic features, which in other word should be stable enough. It is a list of which the parent node has the style “overflow” with value “auto”. And the pseudo element “:active” of the list items has a transform with transition: translateX, so that when finger is on the item, it moves a little bit rightwards. Then the problem came, you can’t scroll the list down directly. If you want to do so, you’ll have to scroll up a little first. And, after you scroll to the bottom, you can even continue scrolling up (though the scrolling becomes no longer smooth).

So I have to change the transform to some older ways, like margin, padding, etc.

2. Scrolling issue with phone:WebBrowser control.

Ohh, again scrolling. To use pointer series of events built in IE, I have set -ms-touch-action to none under html selector. So that the page won’t move around or dance with your fingers. It works well until you get some elements with style “overflow: auto;” (or “scroll” I guess). After a list is scrolled to the top or bottom, if you put away and back your finger and continue scrolling, the whole page starts a bouncing effect. Luckily, this only happens in a phone:WebBrowser control. But unfortunately, I am using that control to wrap up my app.

I guess there should be a solution but I really haven’t found it.

Okay, so these are issues with the front end. On back end, I chose NodeJS as I am more confident with my JS skill. But it really took me quite a lot time to start due to the poor documentation. I am not familiar with back end and everyone knows I write front end all the time. I can’t say it would be a challenge to write the back-end stuffs I’ve done these days, but it was a great chance to learn new things.

The main problem I have is lack of database related experience, I don’t know how the db engine works in the box, so I don’t know how would my structures and logic perform. T-T

But back to NodeJS, I am quite missing my VEJIS, which helps JSers coding without looking for online documentations. Oh dear you know how it feels when you press the “.” but there is no accurate auto completion and hints? It might not be that helpful after one is extremely familiar with a framework, but before that, it helps a lot. And one more thing, we are never be that familiar with every new code line we write…

A thousand words are reserved here…

wb

So, I am very excited as WordsBaking for WP is almost ready for beta, after months. I always believe the power of a team, but when a team is not available, I also believe my own skills. Thanks my CTO (Chief Track Officer) for Tracking my developing progress btw, or I wouldn’t be able to finish the beta version. 🙂