主模塊是與本地代碼交互的接口,它的主要功能有兩個,一個是對BOM提供的document和window的事件偵聽函數進行重定義;另一個是提供了插件回調機制接口函數。
define("myphonegap",function(require, exports, module){
var channel = require("myphonegap/channel");
//文檔對象模型加載監聽事件
document.addEventListener("DOMContentLoaded", function(){
channel.onDOMContentLoaded.fire();
}, false);
if(document.readyState=='complete' || document.readyState == 'interactive'){
channel.onDOMContentLoaded.fire();
//window.app.HelloWorld(" zhangwen ");
}
//將addEventListener和removeEventListener函數保留
var m_window_addEventListener = window.addEventListener;
var m_window_removeEventListener = window.removeEventListener;
var m_document_addEventListener = document.addEventListener;
var m_document_removeEventLiistener = document.removeEventListener;
//緩衝所有事件處理函數
var documentEventHandlers = {},
windowEventHandlers = {};
//重定義函數
window.addEventListener = function(evt,handler,capture){
var e = evt.toLowerCase();
if(typeof windowEventHandlers[e] !== 'undefined'){
windowEventHandlers[e].subscribe(handler);
}else{
m_window_addEventListener.call(window,evt,handler,capture);
}
}
window.removeEventListener = function(evt,handler,capture){
}
document.addEventListener = function(evt,handler,capture){
}
document.removeEventListener = function(evt,handler,capture){
}
function createEvent(type,data){
return event;
}
require("myphonegap/builder");
var myphonegap ={//創建字面量並作爲結果返回
define:define, // 將內部的define作爲myphonegap中一個屬性開放給調用者
require:require,
addWindowEventHandler:function(event, opts) {//添加window事件偵聽,使用內部數組緩存
return (windowEventHandlers[event] = channel.create(event)); //創建新的通道,將新通道對象和事件類型綁定,下面類似
},
addDocumentEventHandler:function(event, opts) {//添加document事件偵聽
return (documentEventHandlers[event] = channel.create(event));
},
addStickyDocumentEventHandler:function(event,opts){
return (documentEventHandlers[event] = channel.createSticky(event));
},
removeWindowEventHandler:function(event) {//移除window事件偵聽
delete windowEventHandlers[event];
},
removeDocumentEventHandler:function(event) {//移除document事件偵聽
delete documentEventHandlers[event];
},
//以對象形式返回DOM中原來定義的事件偵聽函數
getOriginalHandlers: function() {
return {'document': {'addEventListener': m_document_addEventListener, 'removeEventListener': m_document_removeEventListener},
'window': {'addEventListener': m_window_addEventListener, 'removeEventListener': m_window_removeEventListener}};
},
//觸發document事件
fireDocumentEvent: function(type, data) {
},
fireWindowEvent: function(type, data) {
},
/**
* 插件回調機制
* 回調ID採用隨機數避免加載刷新後發生衝突,這樣避免新的回調函數取得和舊回調函數同樣的ID
*/
callbackId: Math.floor(Math.random() * 2000000000),
callbacks: {},
callbackStatus: {
NO_RESULT: 0,
OK: 1,
CLASS_NOT_FOUND_EXCEPTION: 2,
ILLEGAL_ACCESS_EXCEPTION: 3,
INSTANTIATION_EXCEPTION: 4,
MALFORMED_URL_EXCEPTION: 5,
IO_EXCEPTION: 6,
INVALID_ACTION: 7,
JSON_EXCEPTION: 8,
ERROR: 9
},
/**
* 當從執行的本地動作返回一個結果時由本地代碼調用該函數。
*/
callbackSuccess: function(callbackId, args) {
try {
myphonegap.callbackFromNative(callbackId, true, args.status, args.message, args.keepCallback);
} catch (e) {
console.log("Error in error callback: " + callbackId + " = "+e);
}
},
/**
* 本地方法返回失敗時調用該函數
*/
callbackError: function(callbackId, args) {
try {
myphonegap.callbackFromNative(callbackId, false, args.status, args.message, args.keepCallback);
} catch (e) {
console.log("Error in error callback: " + callbackId + " = "+e);
}
},
/**
* 從本地動作返回一個結果時調用該函數
*/
callbackFromNative: function(callbackId, success, status, message, keepCallback) {
var callback = myphonegap.callbacks[callbackId];
if (callback) {
if (success && status == myphonegap.callbackStatus.OK) {
callback.success && callback.success(message);
} else if (!success) {
callback.fail && callback.fail(message);
}
// Clear callback if not expecting any more results
if (!keepCallback) {
delete myphonegap.callbacks[callbackId];
}
}
},
addConstructor: function(func) {
channel.onMyphonegapReady.subscribe(function() {
try {
func();
} catch(e) {
console.log("Failed to run constructor: " + e);
}
});
},
Hello:function(message){
alert("myphonegap hello "+ message);
}
}
channel.onPause = myphonegap.addDocumentEventHandler('pause');
channel.onResume = myphonegap.addDocumentEventHandler('resume');
channel.onDeviceReady = myphonegap.addDocumentEventHandler('deviceready');
module.exports = myphonegap;
console.info('define myphonegap completed');
});
插件的函數回調是與exec這個模塊相關的,在之後介紹exec模塊的時候,會使用到這裏的內容;之所以將回調接口寫在這個模塊對象上是爲了給使用者提供一個顯而易見的接口。本地調用回調JS時就只需訪問myphonegap模塊,不需要再訪問exec這個模塊了。
myphonegap.js本節完成代碼
;(function(){
var require,//myphonegap內部的工具函數,用來導入相關的模塊
define;//在myphonegap註冊相關的模塊
//通過一個立即調用的匿名函數,來給require和define賦上實際的函數
(function(){
var modules={}; // 模塊數組,添加模塊類似給這個對象添加了屬性,模塊名爲屬性名,模塊對象爲屬性值,或者說是鍵值對
build = function(module){ //根據模塊對象構造模塊導出對象,模塊導出對象存儲在modules這個對象數組內
var factory = module.factory;
module.exports = {}; //給當前模塊加入了一個exports屬性
delete module.factory; //刪除了module的屬性
factory(require,module.exports,module); //構建導出模塊,module.exports是傳出參數(實參,引用傳遞)
return module.exports;
}
require = function(id){ //根據模塊名稱/id請求模塊對象,如果是第一次請求,就構建對象
if(!modules[id]){
throw "module " + id + " not found!";
}
return modules[id].factory?build(modules[id]):modules[id].exports;
}
define = function(id,factory){ //定義模塊,模塊名稱、構建模塊對象的工廠方法。
if(modules[id]){
throw "module " + id + " is exist!";
}
modules[id] = { //定義模塊對象,左邊的值爲屬性名,右邊的值爲傳入的參數
id:id,
factory:factory
};
}
})();
//註冊myphonegap模塊
//註冊myphonegap模塊
define("myphonegap", function(require, exports, module){
console.info("create myphonegap module");
var channel = require('myphonegap/channel');
document.addEventListener('DOMContentLoaded', function() {
channel.onDOMContentLoaded.fire();
}, false);
if (document.readyState == 'complete' || document.readyState == 'interactive') {
channel.onDOMContentLoaded.fire();
}
var m_document_addEventListener = document.addEventListener;
var m_document_removeEventListener = document.removeEventListener;
var m_window_addEventListener = window.addEventListener;
var m_window_removeEventListener = window.removeEventListener;
var documentEventHandlers = {},
windowEventHandlers = {};
document.addEventListener = function(evt, handler, capture) {
var e = evt.toLowerCase();
if (typeof documentEventHandlers[e] != 'undefined') {
documentEventHandlers[e].subscribe(handler);
} else {
m_document_addEventListener.call(document, evt, handler, capture);
}
};
window.addEventListener = function(evt, handler, capture) {
var e = evt.toLowerCase();
if (typeof windowEventHandlers[e] != 'undefined') {
windowEventHandlers[e].subscribe(handler);
} else {
m_window_addEventListener.call(window, evt, handler, capture);
}
};
document.removeEventListener = function(evt, handler, capture) {
var e = evt.toLowerCase();
// If unsubscribing from an event that is handled by a plugin
if (typeof documentEventHandlers[e] != "undefined") {
documentEventHandlers[e].unsubscribe(handler);
} else {
m_document_removeEventListener.call(document, evt, handler, capture);
}
};
window.removeEventListener = function(evt, handler, capture) {
var e = evt.toLowerCase();
// If unsubscribing from an event that is handled by a plugin
if (typeof windowEventHandlers[e] != "undefined") {
windowEventHandlers[e].unsubscribe(handler);
} else {
m_window_removeEventListener.call(window, evt, handler, capture);
}
};
function createEvent(type, data) {
var event = document.createEvent('Events');
event.initEvent(type, false, false);
if (data) {
for (var i in data) {
if (data.hasOwnProperty(i)) {
event[i] = data[i];
}
}
}
return event;
}
if(typeof window.console === "undefined") {
window.console = {
log:function(){}
};
}
var myphonegap = {
define:define,
require:require,
/**
*在document和window上添加自定義的事件監聽器方法
* Methods to add/remove your own addEventListener hijacking on document + window.
*/
addWindowEventHandler:function(event) {
return (windowEventHandlers[event] = channel.create(event));
},
addStickyDocumentEventHandler:function(event) {
return (documentEventHandlers[event] = channel.createSticky(event));
},
addDocumentEventHandler:function(event) {
return (documentEventHandlers[event] = channel.create(event));
},
removeWindowEventHandler:function(event) {
delete windowEventHandlers[event];
},
removeDocumentEventHandler:function(event) {
delete documentEventHandlers[event];
},
/**
* 以對象形式返回DOM中原來定義的事件偵聽函數
* @return object
*/
getOriginalHandlers: function() {
return {'document': {'addEventListener': m_document_addEventListener, 'removeEventListener': m_document_removeEventListener},
'window': {'addEventListener': m_window_addEventListener, 'removeEventListener': m_window_removeEventListener}};
},
/**
*從本地代碼觸發事件
*/
fireDocumentEvent: function(type, data, bNoDetach) {
var evt = createEvent(type, data);
if (typeof documentEventHandlers[type] != 'undefined') {
if( bNoDetach ) {
documentEventHandlers[type].fire(evt);
}
else {
setTimeout(function() {
documentEventHandlers[type].fire(evt);
}, 0);
}
} else {
document.dispatchEvent(evt);
}
},
fireWindowEvent: function(type, data) {
var evt = createEvent(type,data);
if (typeof windowEventHandlers[type] != 'undefined') {
setTimeout(function() {
windowEventHandlers[type].fire(evt);
}, 0);
} else {
window.dispatchEvent(evt);
}
},
/**
* 插件回調機制
* 回調ID採用隨機數避免加載刷新後發生衝突,這樣避免新的回調函數取得和舊回調函數同樣的ID
*/
callbackId: Math.floor(Math.random() * 2000000000),
callbacks: {},
callbackStatus: {
NO_RESULT: 0,
OK: 1,
CLASS_NOT_FOUND_EXCEPTION: 2,
ILLEGAL_ACCESS_EXCEPTION: 3,
INSTANTIATION_EXCEPTION: 4,
MALFORMED_URL_EXCEPTION: 5,
IO_EXCEPTION: 6,
INVALID_ACTION: 7,
JSON_EXCEPTION: 8,
ERROR: 9
},
/**
* 本地方法返回成功時調用該函數
*/
callbackSuccess: function(callbackId, args) {
try {
myphonegap.callbackFromNative(callbackId, true, args.status, args.message, args.keepCallback);
} catch (e) {
console.log("Error in error callback: " + callbackId + " = "+e);
}
},
/**
* 本地方法返回失敗時調用該函數
*/
callbackError: function(callbackId, args) {
try {
myphonegap.callbackFromNative(callbackId, false, args.status, args.message, args.keepCallback);
} catch (e) {
console.log("Error in error callback: " + callbackId + " = "+e);
}
},
/**
* 從本地動作返回一個結果時調用該函數
*/
callbackFromNative: function(callbackId, success, status, message, keepCallback) {
var callback = myphonegap.callbacks[callbackId];
if (callback) {
if (success && status == myphonegap.callbackStatus.OK) {
callback.success && callback.success(message);
} else if (!success) {
callback.fail && callback.fail(message);
}
// Clear callback if not expecting any more results
if (!keepCallback) {
delete myphonegap.callbacks[callbackId];
}
}
},
addConstructor: function(func) {
channel.onMyphonegapReady.subscribe(function() {
try {
func();
} catch(e) {
console.log("Failed to run constructor: " + e);
}
});
},
Hello:function(name){
console.info("hello, "+name +" !");
}
};
module.exports = myphonegap;
});
//註冊myphonegap/builder模塊
define("myphonegap/builder", function(require, exports, module) {
var utils = require('myphonegap/utils');
function each(objects, func, context) {
for (var prop in objects) {
if (objects.hasOwnProperty(prop)) {
//console.info(prop);
func.apply(context, [objects[prop], prop]);
}
}
}
function clobber(obj, key, value) {
obj[key] = value;
// Getters can only be overridden by getters.
if (obj[key] !== value) {
utils.defineGetter(obj, key, function() {
return value;
});
}
}
function assignOrWrapInDeprecateGetter(obj, key, value, message) {
if (message) {
utils.defineGetter(obj, key, function() {
console.log(message);
delete obj[key];
clobber(obj, key, value);
return value;
});
} else {
clobber(obj, key, value);
}
}
function include(parent, objects, clobber, merge) {
each(objects, function (obj, key) {
try {
var result = obj.path ? require(obj.path) : {};
if (clobber) {
// Clobber if it doesn't exist.
if (typeof parent[key] === 'undefined') {
assignOrWrapInDeprecateGetter(parent, key, result, obj.deprecated);
} else if (typeof obj.path !== 'undefined') {
// If merging, merge properties onto parent, otherwise, clobber.
if (merge) {
recursiveMerge(parent[key], result);
} else {
assignOrWrapInDeprecateGetter(parent, key, result, obj.deprecated);
}
}
result = parent[key];
} else {
// Overwrite if not currently defined.
if (typeof parent[key] == 'undefined') {
assignOrWrapInDeprecateGetter(parent, key, result, obj.deprecated);
} else {
// Set result to what already exists, so we can build children into it if they exist.
result = parent[key];
}
}
if (obj.children) {
include(result, obj.children, clobber, merge);
}
} catch(e) {
utils.alert('Exception building myphonegap JS globals: ' + e + ' for key "' + key + '"');
}
});
}
function recursiveMerge(target, src) {
for (var prop in src) {
if (src.hasOwnProperty(prop)) {
if (target.prototype && target.prototype.constructor === target) {
// If the target object is a constructor override off prototype.
clobber(target.prototype, prop, src[prop]);
} else {
if (typeof src[prop] === 'object' && typeof target[prop] === 'object') {
recursiveMerge(target[prop], src[prop]);
} else {
clobber(target, prop, src[prop]);
}
}
}
}
}
module.exports = {
buildIntoButDoNotClobber: function(objects, target) {
include(target, objects, false, false);
},
buildIntoAndClobber: function(objects, target) {
include(target, objects, true, false);
},
buildIntoAndMerge: function(objects, target) {
include(target, objects, true, true);
},
recursiveMerge: recursiveMerge,
assignOrWrapInDeprecateGetter: assignOrWrapInDeprecateGetter
};
});
define("myphonegap/channel",function(require,exports,module){
var utils = require("myphonegap/utils"),
nextGuid = 1;
//典型的創建對象方法:通過構造器初始化變量,從而讓各個實例相互獨立;之後通過修改函數原型共享實例方法。
var Channel = function(type,sticky){
this.type = type;
//map of guid -> function
this.handlers = {};
// 0 = Non-sticky, 1 = Sticky non-fired, 2 = Sticky fired.
this.state = sticky?1:0;
// Used in sticky mode to remember args passed to fire().
this.fireArgs = null;
// Used by onHasSubscribersChange to know if there are any listeners.
this.numHandlers = 0;
// Function that is called when the first listener is subscribed, or when
// the last listener is unsubscribed.
this.onHasSubscribersChange = null;
}, channel={
create:function(type){
channel[type] = new Channel(type,false);
},
createSticky:function(type){
channel[type] = new Channel(type,true);
},
deviceReadyChannelsArray: [],
deviceReadyChannelsMap: {},
waitForInitialization: function(feature) {
if (feature) {
var c = channel[feature] || this.createSticky(feature);
this.deviceReadyChannelsMap[feature] = c;
this.deviceReadyChannelsArray.push(c);
}
},
initializationComplete: function(feature) {
var c = this.deviceReadyChannelsMap[feature];
if (c) {
c.fire();
}
},
join: function (h, c) {//join也就是將一組Channel連接起來,並注入一個在所有Channel上只執行一次的公共處理函數
var i = c.length;
var len = i;
var f = function() {
if (!(--i)) h();
};
for (var j=0; j<len; j++) {
!c[j].fired?c[j].subscribe(f):i--;
}
if (!i) h();
}
};
function forceFunction(f){
if (f === null || f === undefined || typeof f != 'function') throw "Function required as first argument!";
}
//給對象Channel的原型對象添加訂閱函數,參數:函數對象、上下文、全局唯一ID
Channel.prototype.subscribe = function(f,c,g){
forceFunction(f);//確保f爲函數
if(this.state==2){ //apply方法能劫持另外一個對象的方法,繼承另外一個對象的屬性;f裏面的指針爲c(Channel的實例)
f.apply(c||this,this.fireArgs);
}
var func = f,
guid = f.observer_guid;
if(typeof f == "object"){ func = utils.close(c,f);}
if(!guid){
guid = ''+ nextGuid++;
}
f.observer_guid = guid;
func.observer_guid = guid;
//防止重複添加
if(!this.handlers[guid]){
this.handlers[guid] = func;
this.numHandlers++;
if(this.numHandlers===1){
this.onHasSubscribersChange&&this.onHasSubscribersChange();
}
}
};
Channel.prototype.unsubscribe = function(f){
forceFunction(f);
var guid = f.observer_guid;
if(this.handlers[guid]){
delete this.handlers[guid];
this.numHandlers--;
if(numHandlers===0){
this.onHasSubscribersChange&&this.onHasSubscribersChange();
}
}
};
//訂閱一次
Channel.prototype.subscribeOnce = function(f,c,g){
};
//調用了在通道訂閱的所有函數
Channel.prototype.fire = function(e){
//console.info('fire start:type/'+this.type + ' numHandlers/' +this.numHandlers);
var fail = false,
fireArgs = Array.prototype.slice.call(arguments);
// Apply stickiness.
if (this.state == 1) {
this.state = 2;
this.fireArgs = fireArgs;
}
if (this.numHandlers) {
// Copy the values first so that it is safe to modify it from within
// callbacks.
var toCall = [];
for (var item in this.handlers) {
toCall.push(this.handlers[item]);
}
for (var i = 0; i < toCall.length; ++i) {
toCall[i].apply(this, fireArgs);
// console.info(this.type+' enter func fire ');
}
if (this.state == 2 && this.numHandlers) {
this.numHandlers = 0;
this.handlers = {};
this.onHasSubscribersChange && this.onHasSubscribersChange();
}
}
};
channel.create('onDOMContentLoaded');
channel.create('onNativeReady');
channel.create('onDeviceReady');
channel.waitForInitialization('onMyphonegapReady');
channel.waitForInitialization('onMyphoneConnectionReady');
module.exports = channel;
//console.info('define myphonegap/channel completed');
});
//註冊myphonegap/common模塊
//配置對象,將公共模塊組織起來
define("myphonegap/common",function(require,exports,module){
module.exports = {
defaults: {
myphonegap: {
path: 'myphonegap',
children: {
exec: {
path: 'myphonegap/exec'
}
}
},
Myphonegap: {
children: {
exec: {
path: 'myphonegap/exec'
}
}
}
},
clobbers: {
navigator: {
children: {
connection: {
path: 'myphonegap/plugin/network'
}
}
}
}
};
});
define("myphonegap/exec", function(require, exports, module) {
module.exports= function(name){
console.info("Exec native function " + name+" !");
};
});
//註冊myphonegap/platform模塊
define("myphonegap/platform", function(require, exports, module){
});
// 這裏省略了其它插件的註冊
//註冊myphonegap/utils模塊
define("myphonegap/utils", function(require, exports, module){
var utils = exports;
utils.defineGetterSetter = function(obj, key, getFunc, opt_setFunc) {
if (Object.defineProperty) {
var desc = {
get: getFunc,
configurable: true
};
if (opt_setFunc) {
desc.set = opt_setFunc;
}
Object.defineProperty(obj, key, desc);
} else {
obj.__defineGetter__(key, getFunc);
if (opt_setFunc) {
obj.__defineSetter__(key, opt_setFunc);
}
}
};
utils.defineGetter = utils.defineGetterSetter;
utils.alert = function(msg) {
if (window.alert) {
window.alert(msg);
} else if (console && console.log) {
console.log(msg);
}
};
});;
(function (context) {
}(window));
//所有模塊註冊完之後,再導入myphonegap至全局環境中
//window.myphonegap = require('myphonegap');
//window.myphonegap.Hello("wen");
//測試channel模塊
/*
var channel = require("myphonegap/channel");
channel.onNativeReady.subscribe(function(){
console.info("onNativeReady");
}
);
console.info("before native ready");
channel.onNativeReady.fire();
console.info("after native ready");
*/
//測試common模塊與builder模塊
var builder = require("myphonegap/builder");
var common = require("myphonegap/common");
builder.buildIntoButDoNotClobber( common.defaults,window);
window.myphonegap.Hello("Jack");
window.myphonegap.exec("myfunc");
})();