Sunday, June 23, 2013

cordova.js javascript logic

One of the things that makes cordova confuse is it uses the same name for both function parameter and variable.

When cordova.js is called, the outmost function first set the cordova version number and then declare two closure function variables: define and require.
And then it executes another inner function, which implements the define and require function, and modules variable exposed by define.moduleMap.
Up to now, the following variables have been defined inside function scope:
define
require
define.moduleMap
define.remove
When require function is called, it returns the module.exports, which is usually a constructor function to create the module instance. (71:   return modules[id].exports;)

Next, it call define method on cordova and other modules, which will put those modules in modules list. The factory function is not called yet.
define("cordova", function(require, exports, module)...);

define("cordova/argscheck", function(require, exports, module)
define("cordova/channel", function(require, exports, module) 
.....
Then it loads the cordova module with the following line:
window.cordova = require('cordova');
    function build(module) 
    factory:function(require, exports, module). In the factory method, require is the input parameter, container the define function. module is the input parameter represent the current module. exports is the output parameter containing the exported function for the module.
 
The cordova module factory function then load channel by requires it with following code
102: var channel = require('cordova/channel');

For channel:
Note the line 102, var channel = require('cordova/channel');  The var channel contains the exported       javascript object returned to channel.exports. The Channel defined in line
var Channel = function(type, sticky) {
is the prototype construtor for creating an individual channel object, which can handle a particular event
514: var utils = require('cordova/utils'),
After utils module is loaded, channel module register events with the following calls
722: channel.createSticky('onDOMContentLoaded');
725: channel.createSticky('onNativeReady');

107: after channel module is loaded,  cordova module can hookup document event with channel event
186: var cordova = { create the cordova object as exported javascript object
328: expose cordova module by: module.exports = cordova;

6255: (function (context) { call the context method on window object, it will require and load all modules, like contact, and file



 

Saturday, June 22, 2013

cordova ios native code logic

For cordova ios project created by cordova create command, the application and plugin starting up includes the following logic:
1.  AppDelegate
In didFinishLaunchingWithOptions, it initializes MainViewController, which is a subclass of CDVViewController. The init of CDVViewController will hook up cordova function with application.

2. CDVViewController
In CDVViewController, __init method, it first registers all application notification event, so that it can relay those event to javascript. Then it calls loadSettings, which will parse config.xml which defines the cordova behavior, including starting page and plugin definition.
The CDVViewController owns all plugins instance after the instances are created. That is the plugin lives within CDVViewController's scope and lifetime. However, it is possible, the same webview loads multiple html js app, in that case, the plugin's state in native code will not be reset automatically when new page loads. For this reason, CDVPlugin interface defines a method onReset to clean up the plugin state from js page to page. This method should be implemented by each plugin.

The CDViewController has an important method createGapView, which is called in viewDidLoad method. This method creates UIWebView control, and also sets CDVViewDelegate to the UIWebView instance, to handle the page event. The createGapView method also hooks up with javascript request using CDVURLProtocol (a sub class of NSUrlProtocol).  When a javascript method is called,

3. plugin
Usually, plugin will not create its instance until its method is called the first time. But it can also create its instance during ApplicationDidFinishingWithOption by setting onLoad attribute in config.xml. If so,  the PluginInitialize method can be implemented to register the application events including openUrl, etc. Some basic event it registered by its base class CDVPlugin interface, so do not register those event again.
The plugin method is alway called from the main thread.




Friday, June 21, 2013

xcode iphone project interface name conflict

When an ios xcode application uses more than one static libraries, if the libraries include the same interface with their own implementation, then when application creates the interface instance, a conflict will happen, as it cannot decide which library's implementation should be used.
However, the strange thing is xcode does not give a warning or error when building the application when this happens. The error will happen in runtime, it seems xcode just pick one of the implementation to continue.
The correct way to handle it is create a base library that includes the shared common source interface, and implementation, and then reference it from all other libraries and the application.

Monday, June 17, 2013

Understanding "this" and its scope in javascript


This in javascript code is a confusing concept, and needs few rules to clarify it:
1. only use this in function definition, this does not work in object initialization
var obj = {
    a: 5,
    b: this.a + 1 
}
alert(obj.b);//b is undefined
2. even if a method is defined as a property of an object, when the method is called, the this may point to a different object, this different from C++/C# or java.
function test(){
var a ={
 answer : "a",
 func : function(){
  alert(this ==a);
 }
};
a.func();  //this inside func is a
var b = a.func;
b.call(b); //this inside func is b
}

3. Within the function body, if the function is invoked as myfunc();, then the global window object is used as this.
var a = {a:"parent"};
a.func =function(){
alert(this);
}
a.func(); //this is a inside func
var b = a.func;
b(); //this is window inside func, even if it refers a property function of object a

4. if the method is invoked with a dot as myobj.mymMethod();, then the myobj is used as this, except for constructor invocation described in the next two item (item 5 and 6)

5. when function is invoked as constructor with "new", such as myfunct() or new myobject.myfunc(), then this is the new object created for running the function. Basically, it first create a new object and set the new object as this for invoking the function. It also explains the difference between two ways to call a method:
funcA();
with
new funcA() or new objA.funcA();
as the this points to different objects in the two cases.

6. if the method or function is invoked as myMethod.call(yourObj, ...) or myObject.myMethod.call(yourObj) or myMethod.apply(yourobj) or myObject.myMethod.app(yourObj), then the yourObj is used as this, even if myObject is used to invoke the method.

7. Note that although inner function can access all variables defined in the parent function or object, it may not access the variables defined as this.parentvariable, as "this" for inner function may be different from "this" in the parent function.

8. It is obvious to see the difference on html element between
onclick=externaljsfunction;
and
onclick="externaljsfunction();"

when onclick is called, its "this" is the current html element, so in first case, externaljsfunction has the html element as its this. In second case, when externaljsfunction is called, this is set to window, unless change it to onclick="externaljsfunction.call(this)";

9. what is use of the below code
self = this;
The only purpose of this code is keeping track the this object so as to use it later by inner function. As when inner function or callback method is called, this object may be set to any value, the only way to get the parent function's this is assigning it to a variable in parent function, and then get it back using closure

var user = {
    tournament:"The Masters",
    data      :[
         {name:"T. Woods", age:37},
         {name:"P. Mickelson", age:43}
    ],

    clickHandler:function (event) {
    // set "this" to theUserObj variable, so we can use it later
    var self = this;
    this.data.forEach (function (person) {
// this now points to window object, but we can use theUserObj.tournament
console.log (person.name + " is playing at " + self.tournament);
      });
    }
};
user.clickHandler();

10.  bind this with setTimeout
setTimeout can be used to wrap synchronous call to asynchronous call. By default, when a method is called through setTimeout(), the "this" keyword will be set to the window object. In order to keep the original this object when setTimeout is called, you can explicitly bind the original this to the callback function
var a={
name:"myname",
mycall:function(){
console.log(this.name);
},
myasyncCall:function(){
setTimeout(this.mycall.bind(this), 0);
},
myasyncCallWrong:function(){
setTimeout(this.mycall, 0);
},
};
a.mycall();
a.myasyncCallWrong();  //this is window when mycall is invoked
a.myasyncCall(); //this is object a

11.sample code to use constructor parameter
function Person(gender) {
  if (arguments.length == 0) return; // do nothing if no paras for subclass constructor
  this.gender = gender;
}

function Student(gender) {
  Person.apply(this, arguments); //apply paras to parent constructor
}
Student.prototype = new Person(); // make Student inherit from a Person object
Student.prototype.constructor = Student; // fix constructor property

var foo = new Student('male');
foo.gender;             // "male"
foo instanceof Student; // true
foo instanceof Person;  // true

12. Similarly rule also applies to the variable, if it is referred as myObject.myVariable, then myVariable is searched from myObject's properties. if it is referred as myVariable directly, then it will search the global object's properties (i.e the window object)

13. Difference between method declared as arrow expression
When method is defined using arrow expression, this is hardcoded to bind to the this when the method is created, and ignores the this set to it at runtime. 
class myComponent {
  onTempTest2() {
    const obj = {
      name: 'Jonathan',
      func(){
        console.log(this);
      },
      myfunc : () => {
        console.log(this); //this is hard coded to this instance when the method is defined
      }
    };
    obj.func(); //inside func method invocation, this is obj
    obj.myfunc(); //inside myfunc method invocation, this is instance of myComponent

    const t = obj.func;
    const t2 = obj.myfunc;

    t(); // inside func invocation, this is undefined
    t2(); // inside myfunc invocation, this is instance of myComponent


    t.call(window); //this is window object
    t2.call(window); //this is instance of myComponent
    
//current this myComponent instance
t.call(this); // this is instance of myComponent, referred from parameter
    t2.call(this); //this is instance of myComponent referred from arrow function context

setTimeout(() => { t(); //inside func invocation, this is undefined }, 100); setTimeout(() => { t2(); //inside myfunc invocation, this is myComponent }, 100);
}