6 (phonegap源码分析)主模块的实现 (myphonegap)

来源:互联网 发布:乐乎pt上不去 编辑:程序博客网 时间:2024/06/07 10:42

        主模块是与本地代码交互的接口,它的主要功能有两个,一个是对BOM提供的documentwindow的事件侦听函数进行重定义;另一个是提供了插件回调机制接口函数。

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 resultsif (!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 pluginif (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 pluginif (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 resultsif (!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 -> functionthis.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的原型对象添加订阅函数,参数:函数对象、上下文、全局唯一IDChannel.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");})();

原创粉丝点击